---
title: 'Uygulama Katmanı Protokolleri'
source: 'https://academia.sh/tr/kurslar/uygulama-protokolleri'
language: tr
updated: '2026-08-17T04:39:03+00:00'
license: 'CC BY-SA 4.0'
lessons: 16
outcomes:
    - 'HTTP sürümleri arasındaki tasarım farklarını başarım açısından açıklar'
    - 'TLS el sıkışmasının adımlarını ve sertifika zincirini okur'
    - 'Gerçek zamanlı iletişim için uygun protokolü seçer'
---

# Uygulama Katmanı Protokolleri

HTTP ailesinin yöntem, durum, başlık ve önbellekleme kuralları; güvenli taşımanın anahtar, sertifika ve el sıkışma zinciri; gerçek zamanlı, dosya, posta ve yönetim protokolleri — her biri aradaki aracının iletiden alabildiği karar üzerinden ölçülür.

## HTTP Ailesi

Yöntemlerin taşıdığı taahhüt, durum kodlarının sınıfları, başlıklarla içerik pazarlığı, çerezlerin kapsam kuralları, sürümler arası çerçeveleme farkı ve önbellek doğrulaması.

### HTTP Yöntemleri ve Anlamları

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/http-yontemleri-ve-anlamlari

Yöntem bir bildirimdir: aradaki aracı ondan yinelenebilir ve saklanabilir kararını çıkarır ve bildirimi doğrulayan hiçbir şey yoktur.

Ağ Modelleri ve Protokoller kursu taşıma katmanını (transport layer) bitirirken elde tek bir
söz kalmıştı: TCP sıralı, kayıpsız, yinelemesiz bir bayt akışı verir. Bu sözün içinde bir
boşluk durur. Taşıma katmanı **baytı** taşır; baytın ne anlama geldiğini bilmez. Aynı kırk
bayt bir ölçüm kaydı da olabilir, bir silme buyruğu da; TCP ikisini birbirinden ayırmaz ve
ayırmayı üstlenmez.

Anlamı **uygulama katmanı** kurar. Ve anlamı kuran şey, iletide yazandan başkası değildir:
bir yöntem adı, bir yol, bir başlık alanı (header field). Bu kurs boyunca sayılacak şey de
budur — iletide yazanın, iletiyi okuyan üçüncü bir tarafa kaç kararı aldırdığı. Bu ders önce
o üçüncü tarafı ve ölçüyü kurar, sonra ilk alanı ölçer: yöntemi.

#### Aracı: Uygulamayı Bilmeyen Katman

Kuzey Yamaç ölçüm istasyonu, ölçümlerini bir istemciye sunar. İkisinin arasında bir **aracı
(intermediary)** durur: önbellekleyen, yönlendiren, gerektiğinde yeniden deneyen bir katman.
Vekil (proxy) aracının bir türüdür; genel ad aracıdır.

Aracı uygulamayı **bilmez.** Ne ölçüldüğünü, kaydın ne anlama geldiğini, hangi oturumun
kime ait olduğunu bilmez. Bildiği tek şey iletide yazandır. Kursun bütün konusu bu tek
cümlede toplanır: **iletide ne yazıyorsa aracı onu bilir; yazmıyorsa bilmez; yanlış yazıyorsa
yanlış bilir.**

Aracının gördüğü istek şuna benzer. Aşağıdaki döküm öğretim amaçlıdır ve çalıştırılmaz:

```text
POST /kayit HTTP/1.1
Host: istasyon.ornek
Content-Type: application/json
Content-Length: 74
```

Yanıt tarafında aracının okuduğu iki im daha vardır. Yanıtın **ilk satırı** sonraki dersin
konusudur; burada yalnız başlık alanları okunur:

```text
Cache-Control: private
ETag: "s-0417"
```

Bu dört satırın modeldeki karşılıkları şunlardır:

| Dökümdeki yer | Modeldeki alan | Aracının çıkardığı |
|---|---|---|
| İstek satırının ilk sözcüğü | `yontem` | `yinelenebilir`, `saklanabilir` |
| İstek satırının ikinci sözcüğü | `yol` | kaynağın kimliği |
| `Host` | `konak` | `yonlendirilebilir` |
| `Cache-Control: private` | `ozel_imi` | `paylasilabilir`, `saklanabilir` |
| `ETag` | `dogrulayici` | `taze` |

#### Yöntem Bir Bildirimdir

Yöntem iki ayrı özellik bildirir. Bir yöntem **güvenli (safe)** ise, isteği göndermek
sunucudaki durumu değiştirmeyi amaçlamaz. Bir yöntem **etkisiz (idempotent)** ise, aynı
isteği bir kez ya da art arda beş kez göndermek aynı sonucu bırakır.

| Yöntem | Güvenli | Etkisiz | Aracının çıkardığı |
|---|---|---|---|
| GET | evet | evet | yanıt saklanabilir, istek yinelenebilir |
| HEAD | evet | evet | yanıt saklanabilir, istek yinelenebilir |
| PUT | hayır | evet | yanıt saklanmaz, istek yinelenebilir |
| DELETE | hayır | evet | yanıt saklanmaz, istek yinelenebilir |
| POST | hayır | hayır | ikisi de değil |

Güvenlilik etkisizliği içerir: durumu değiştirmeyen bir istek, yinelendiğinde de
değiştirmez. Tersi doğru değildir; PUT durumu değiştirir ama aynı gövdeyle yinelendiğinde
aynı durumu bırakır.

Web API Tasarımı kursunun kaynak tasarımı konusu bu ayrımı **tasarımcının taahhüdü** olarak
ele alır ve hangi yöntemin seçilmesi gerektiğini söyler. Burada seçim çoktan yapılmıştır;
ölçülen şey, yapılmış seçimin **aracıya ne bildirdiğidir**. Etkisizlik anahtarı da aynı
kursta kurulmuştur ve bir uygulama sözleşmesidir; bu ders protokolün kendi bildirimini
ölçer, uygulamanın eklediğini değil.

#### Ölçü: Fazladan Tur Olmadan Alınan Karar

Bir protokolün sayısı, taşıdığı alan değil, alıcı tarafa **fazladan bir gidiş–dönüş (round
trip) olmadan kaç kararı aldırdığıdır.** Alınamayan bir karar bir tur borcudur: aracı
bilemediği şeyi sormak zorunda kalır ve sormak bir tur eder.

Kurgu kırk alışveriş üretir. Her alışverişte aracının vermesi istenen beş karar vardır.

| Karar | Sorusu |
|---|---|
| `saklanabilir` | Bu yanıt önbelleğe konabilir mi |
| `paylasilabilir` | Saklanan kopya başka bir istemciye verilebilir mi |
| `taze` | Elde tutulan kopya hâlâ geçerli mi |
| `yinelenebilir` | İstek yanıtsız kalırsa yeniden gönderilebilir mi |
| `yonlendirilebilir` | İleti hedefine yönlendirilebilir mi |

Kırk alışveriş ve beş karar, iki yüz kararlık bir kümedir. Bu küme ölçümün **çözünürlüğünü**
de belirler: burada ölçülebilen en küçük fark $1/200 = 0{,}0050$'dir ve bundan küçük bir
fark iddia edilmez.

Gerçeğin kendisi **kâhin (oracle)** ile temsil edilir. Kurguyu biz ürettiğimiz için her
alışverişin gerçekten kişiye özel olup olmadığı ve kopyanın gerçekten bayatlayıp
bayatlamadığı bilinir. Aracının kararı bu gerçeğe göre doğru, yanlış ya da alınamayan diye
sayılır.

Ölçümün varsayımları:

- **HA1** — Kırk alışveriş tek bir tohumdan üretilir; yöntem ve yol sabit listelerden
  çekilir ve ölçüm her koşumda aynı kümeyi verir.
- **HA2** — Kâhinin gerçeği kurguyu biz ürettiğimiz için bilinir: yanıtın kişiye özel olup
  olmadığı ve kopyanın bayatlayıp bayatlamadığı üretim anında belirlenir, iletiden okunmaz.
- **HA3** — Bu derste tek bir rejim ölçülür: ileti **açıktır** ve beş alanın beşi de aracıya
  görünür. Alanın gizlendiği rejimler sonraki derslerin konusudur.
- **HA4** — Aracı uygulamayı bilmez ve her kararı yalnız iletide bulunan alandan kurar. Alan
  iletide yoksa karar hiç verilmez — yanlış karar değil, **alınamayan** karar.
- **HA5** — Kişiye özel im sunucunun bildirimidir ve sunucu kimi kişiye özel yanıtta onu
  koymayı atlar; doğrulayıcı damganın da bir çözünürlüğü vardır ve kimi değişimi kaçırır.
  **İletide yazanı doğrulayan hiçbir şey yoktur.**
- **HA6** — Bayt sayımı alan adı, değer ve ayraç uzunluğundan hesaplanır; ölçüm bir ağ
  ölçümü değil, ileti içeriğinin sayımıdır.

#### Çekirdek

Kurgu, kâhin ve aracı aşağıdaki çekirdekte kurulur. `ACIK` demeti, aracının okuyabildiği
alanları verir: bu derste ileti açıktır, beş alanın beşi de görünür.

```python
TOHUM = 20260809
GUVENLI = {"GET", "HEAD"}
ETKISIZ = {"GET", "HEAD", "PUT", "DELETE"}
YONTEMLER = ["GET", "GET", "GET", "HEAD", "POST", "PUT", "DELETE"]
YOLLAR = ["/olcum/kuzey", "/olcum/yamac", "/ozet", "/kayit", "/oturum"]
KARARLAR = ("saklanabilir", "paylasilabilir", "taze", "yinelenebilir",
            "yonlendirilebilir")
ACIK = ("yontem", "yol", "konak", "ozel_imi", "dogrulayici")


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def alisverisler(sayi: int = 40, tohum: int = TOHUM) -> list[dict]:
    r, liste = uretec(tohum), []
    for i in range(sayi):
        yontem, yol = YONTEMLER[r(7)], YOLLAR[r(5)]
        ozel = yol == "/oturum" or r(5) == 0
        degisti = r(3) == 0
        liste.append({
            "no": i + 1, "yontem": yontem, "yol": yol,
            "ozel": ozel,                           # gerçek: yanıt kişiye özel
            "degisti": degisti,                     # gerçek: kopya bayat
            "ozel_imi": ozel and r(4) != 0,         # bildirim: sunucu im koydu
            "damga_atladi": degisti and r(6) == 0,  # doğrulayıcı değişimi kaçırdı
        })
    return liste


def kahin(a: dict) -> dict:
    return {
        "saklanabilir": a["yontem"] in GUVENLI and not a["ozel"],
        "paylasilabilir": not a["ozel"],
        "taze": not a["degisti"],
        "yinelenebilir": a["yontem"] in ETKISIZ,
        "yonlendirilebilir": True,
    }


def ileti(a: dict, gorunur: tuple) -> dict:
    tam = {"yontem": a["yontem"], "yol": a["yol"], "konak": "istasyon.ornek",
           "ozel_imi": a["ozel_imi"],
           "dogrulayici": a["degisti"] and not a["damga_atladi"]}
    return {k: v for k, v in tam.items() if k in gorunur}


def bayt(i: dict) -> int:
    return sum(len(k) + len(str(v)) + 4 for k, v in i.items())


def araci(i: dict) -> dict:
    k = {}
    if "yontem" in i:
        k["yinelenebilir"] = i["yontem"] in ETKISIZ
    if "ozel_imi" in i:
        k["paylasilabilir"] = not i["ozel_imi"]
        if "yontem" in i:
            k["saklanabilir"] = i["yontem"] in GUVENLI and not i["ozel_imi"]
    if "dogrulayici" in i:
        k["taze"] = not i["dogrulayici"]
    if "konak" in i:
        k["yonlendirilebilir"] = True
    return k
```

`araci` işlevinin yapısı dikkat ister: her karar bir `in` denetiminin arkasındadır. Alan
iletide yoksa karar sözlüğe hiç girmez — aracı yanlış karar vermez, **karar veremez.** İki
sonuç arasındaki bu ayrım kursun geri kalanında sayılacaktır.

#### Açık Rejimin Ölçümü

Ölçüm, yukarıdaki çekirdeğin ardından koşar. Her alışverişte kâhinin gerçeği ile aracının
kararı karşılaştırılır; bir karar hiç alınamadıysa o alışveriş bir tur borçlanır.

```python
alis = alisverisler()
sayac = {k: [0, 0, 0] for k in KARARLAR}    # doğru, yanlış, alınamayan
tur = bayt_toplam = 0
for x in alis:
    gercek, verilen = kahin(x), araci(ileti(x, ACIK))
    eksik = False
    for karar in KARARLAR:
        if karar not in verilen:
            sayac[karar][2] += 1
            eksik = True
        elif verilen[karar] == gercek[karar]:
            sayac[karar][0] += 1
        else:
            sayac[karar][1] += 1
    tur += 1 if eksik else 0
    bayt_toplam += bayt(ileti(x, ACIK))

print(f"alışveriş {len(alis)}, karar {len(alis) * len(KARARLAR)}, "
      f"etkisiz bildiren {sum(x['yontem'] in ETKISIZ for x in alis)}, "
      f"etkisiz bildirmeyen {sum(x['yontem'] not in ETKISIZ for x in alis)}, "
      f"güvenli bildiren {sum(x['yontem'] in GUVENLI for x in alis)}")
print(f"kişiye özel {sum(x['ozel'] for x in alis)}, "
      f"imsiz kalan {sum(x['ozel'] and not x['ozel_imi'] for x in alis)}, "
      f"bayat {sum(x['degisti'] for x in alis)}")
print(f"açık rejim: doğru {sum(v[0] for v in sayac.values())}, "
      f"yanlış {sum(v[1] for v in sayac.values())}, "
      f"alınamayan {sum(v[2] for v in sayac.values())}, "
      f"fazladan tur {tur}, bayt {bayt_toplam}")
print()
print(f"{'karar':<18s} {'doğru':>6s} {'yanlış':>7s} {'alınamayan':>11s}")
for karar, (d, y, e) in sayac.items():
    print(f"{karar:<18s} {d:6d} {y:7d} {e:11d}")
```

```
alışveriş 40, karar 200, etkisiz bildiren 31, etkisiz bildirmeyen 9, güvenli bildiren 18
kişiye özel 17, imsiz kalan 2, bayat 12
açık rejim: doğru 195, yanlış 5, alınamayan 0, fazladan tur 0, bayt 3524

karar               doğru  yanlış  alınamayan
saklanabilir           40       0           0
paylasilabilir         38       2           0
taze                   37       3           0
yinelenebilir          40       0           0
yonlendirilebilir      40       0           0
```

Okunacak ilk satır **alınamayan 0** ve **fazladan tur 0**. İleti açıkken aracı iki yüz
kararın iki yüzünü de alır ve sunucuya bir kez bile dönmez. Üç bin beş yüz yirmi dört baytın
karşılığı budur.

İkinci satır yöntemin payıdır. Kırk alışverişin **otuz biri** etkisiz bir yöntem bildirir,
**on sekizi** güvenli bir yöntem bildirir. `yinelenebilir` kırk kararın kırkında doğrudur,
çünkü kâhin de aracı da aynı kümeye bakar: yöntem adı iletide yazılıdır ve başka hiçbir
şeye bağlı değildir. Yanıtsız kalan bir istekte aracının yeniden gönderip göndermeyeceğine
karar veren tek şey bu tek sözcüktür.

`saklanabilir` de kırk kararın kırkında doğrudur, ama gerekçesi aynı değildir. Bu karar iki
alana birden bağlıdır: yöntem güvenli olmalı **ve** yanıt kişiye özel olmamalıdır. Kırk
alışverişin **on yedisi** gerçekten kişiye özeldir. Aracı bunu yöntemden çıkaramaz;
sunucunun koyduğu ime bakar.

#### Yinelemenin Bedeli

`yinelenebilir` kararının kırkında da doğru çıkması onun bedelsiz olduğu anlamına gelmez;
karşılığı olan durumda pahalıdır. Bir istek gönderildi ve yanıt gelmedi diyelim. Aracının
bildiği tek şey yanıtın gelmediğidir. İsteğin sunucuya hiç ulaşmadığını mı, yoksa ulaşıp
işlendiğini ve yanıtın dönüş yolunda kaybolduğunu mu, ayırt edemez. Taşıma katmanı bu
ayrımı vermez: kopan bir bağlantı iki durumda da aynı görünür.

Yöntem etkisizse ayrım gerekmez. İstek ister ulaşmış ister ulaşmamış olsun, ikinci kez
göndermek aynı durumu bırakır; aracı kimseye sormadan yeniden gönderir. Kırk alışverişin
**otuz birinde** durum budur ve karar fazladan tur olmadan alınır.

Kalan **dokuz** alışverişte yöntem POSTtur. Aracı burada yeniden gönderemez, çünkü ikinci
gönderim ilk gönderimin sonucunu iki katına çıkarabilir. Yapabileceği tek şey kararı uca
taşımaktır: durumu istemciye bildirmek ya da sunucuya sormak. İkisi de bir gidiş–dönüştür.
Tek bir sözcük, otuz bir alışverişte turu sıfırlar, dokuzunda ödetir.

Ölçünün ilk somut karşılığı budur. Aynı alan aynı yerde durur; taşıdığı değere göre bir
kararı ya iletinin içinde kapatır ya da bir tur borcuna çevirir. Protokolün sayısı, bu iki
sonucun kırk alışveriş boyunca nasıl dağıldığıdır.

#### Eksik İm Neden Dört Değil İki Yanlış Doğurur

Kişiye özel on yedi yanıtın **ikisinde** sunucu imi koymamıştır. Bu iki alışverişte aracı
elindeki tek bildirime inanır ve yanıtı paylaşılabilir sayar. Ama yanlış sayısı dört değil
ikidir. Nedeni aşağıdaki dökümdedir; çekirdek ve ölçüm bloklarının ardından koşar:

```python
print(f"{'imsiz kalan':<21s} {'paylasilabilir':>21s} {'saklanabilir':>21s}")
print(f"{'no':>3s} {'yöntem':<7s} {'yol':<9s} {'verilen':>10s} {'gerçek':>10s} "
      f"{'verilen':>10s} {'gerçek':>10s}")
for x in alis:
    if x["ozel"] and not x["ozel_imi"]:
        g, v = kahin(x), araci(ileti(x, ACIK))
        print(f"{x['no']:3d} {x['yontem']:<7s} {x['yol']:<9s} "
              f"{str(v['paylasilabilir']):>10s} {str(g['paylasilabilir']):>10s} "
              f"{str(v['saklanabilir']):>10s} {str(g['saklanabilir']):>10s}")
```

```
imsiz kalan                  paylasilabilir          saklanabilir
 no yöntem  yol          verilen     gerçek    verilen     gerçek
  1 POST    /kayit          True      False      False      False
 34 POST    /kayit          True      False      False      False
```

İki alışverişin de yöntemi **POST**tur. POST güvenli değildir; aracı onun yanıtını im olsun
olmasın zaten saklamaz. `saklanabilir` bu yüzden ikisinde de **doğru** çıkar — doğru
gerekçeyle değil, ama doğru. `paylasilabilir` ise yalnız ime bağlıdır ve ikisinde de yanlış
çıkar.

Buradan çıkan kural ölçünün kendisi kadar önemlidir: **eksik bir bildirim, ancak başka
türlü karar verilebilecek bir yerde bedel ödetir.** Aynı iki alışverişin yöntemi GET olsaydı
`saklanabilir` de yanlışa düşerdi ve toplam yanlış beş değil yedi olurdu. İki alanın
kesiştiği kararlar, tek alana bağlı kararlardan daha dayanıklıdır; çünkü ikinci alan
birinciyi örtebilir.

Kalan üç yanlış `taze` kararındandır. Kırk kopyanın on ikisi gerçekten bayatlamıştır;
bunların **üçünde** doğrulayıcı damga değişimi yakalayamamıştır ve aracı bayat kopyaya taze
demiştir. Doğrulayıcının çözünürlüğü bu kursun ilerleyen bir dersinin konusudur; burada
yalnız yanlış sayısına katkısı sayılır: iki artı üç, **beş**.

#### Bildirimi Doğrulayan Yoktur

Beş yanlışın ikisi sunucunun koymadığı bir imden, üçü bir damganın çözünürlüğünden gelir.
Ortak yanları şudur: **hiçbiri aracının hatası değildir.** Aracı iletide yazanı okumuş ve
doğru işlemiştir. Yanlış, iletide yazanla gerçeğin ayrılmasından doğar.

Protokolde bu ayrımı kapatan bir düzenek yoktur. Aracı, bir yöntemin gerçekten güvenli olup
olmadığını sınayamaz; POST bildiren bir isteğin sunucuda hiçbir şeyi değiştirmediğini ya da
GET bildiren bir isteğin bir kaydı sildiğini göremez. Elindeki tek şey bildirimdir ve
**aracı bildirime inanır.**

Bunun bir de daraltma tarafı vardır. Sunucu bir alanı iletiye koymadığında aracı o alana
bağlı kararı **veremez**; koyduğunda ise aracı o alanı sorgusuz kullanır. İki uç arasındaki
seçim, sonraki derslerde tek tek ölçülecek olan şeydir: hangi alan görünür kalırsa aracı ne
kazanır, görünmez olursa ne kaybeder.

#### Özet

- Taşıma katmanı baytı taşır ve anlamını bilmez; anlamı uygulama katmanı kurar ve o anlamı
  kuran şey yalnızca iletide yazandır.
- Bir protokolün ölçüsü taşıdığı alan sayısı değil, fazladan bir gidiş–dönüş olmadan
  aldırdığı karar sayısıdır; alınamayan karar bir tur borcudur.
- Yöntem iki şey bildirir: güvenlilik ve etkisizlik. Aracı bunlardan `yinelenebilir` ve
  `saklanabilir` kararlarını çıkarır.
- Kırk alışveriş ve beş kararlık kümede açık rejim 195 doğru, 5 yanlış, 0 alınamayan karar
  ve 0 fazladan tur verir; ölçüm 3524 bayt okur.
- Beş yanlışın ikisi imsiz kalan iki yanıttandır ve dört değil ikidir, çünkü o iki
  alışverişin yöntemi zaten güvenli değildir; kalan üçü doğrulayıcı damgasının kaçırdığı
  değişimlerden gelir.
- İletide yazanı doğrulayan hiçbir düzenek yoktur; aracı bildirime inanır ve yanlışlar bu
  güvenden doğar.

#### Sonraki Adım

Bu derste ölçülen alanların hepsi isteğin tarafındaydı: yöntem, yol, konak. Yanıtın
başlıklarına bakıldı ama **ilk satırı** bilerek dışarıda bırakıldı. Oysa aracının yanıt
üzerinde alacağı ilk karar o satırdadır ve satırın taşıdığı şey üç basamaklı bir sayıdır.
Sonraki ders bu sayıyı ölçer: aracı gövdeyi hiç açmadan, yalnız ilk basamağa bakarak yeniden
deneyip denemeyeceğine, saklayıp saklamayacağına karar verebilir mi — ve gövdeyi açan bir
aracı aynı kararı alırken kaç bayt fazla okur.

### Durum Kodları

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/durum-kodlari

Durum satırının ilk basamağı bir yönergedir: aracı gövdeyi hiç açmadan saklama, yönlendirme ve yeniden deneme kararlarını ondan çıkarır.

Önceki ders isteğin alanlarını ölçtü: yöntem, yol, konak. Yanıtın başlık alanlarına bakıldı
ama **ilk satırı** bilerek dışarıda bırakıldı. Aracının bir yanıt üzerinde alacağı ilk karar
o satırdadır ve satırın taşıdığı şey üç basamaklı bir sayıdır.

Bu dersin sorusu şudur: aracı, yanıtın gövdesini hiç açmadan, yalnız o sayının **ilk
basamağına** bakarak ne kadar ileri gidebilir. Gövdeyi de açan bir aracı aynı kararlara mı
varır — varıyorsa fazladan okuduğu baytın karşılığı nedir.

#### Durum Satırı

Yanıt, bir durum satırıyla başlar. Aşağıdaki döküm öğretim amaçlıdır ve çalıştırılmaz:

```text
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 128

HTTP/1.1 301 Moved Permanently
Location: /olcum/kuzey/guncel

HTTP/1.1 503 Service Unavailable
Retry-After: 30
```

Satırın üç parçası vardır: protokolün adı ve sürümü, üç basamaklı **durum kodu** ve kodun
insan okuması için yazılmış kısa karşılığı. Aracının işine yarayan tek parça ortadaki
sayıdır; sağdaki metin bilgilendirmedir ve karara girmez.

Sayının kendisi de bütün olarak gerekmez. İlk basamak kodun **durum sınıfını (status
class)** verir ve aracının okuduğu asıl şey budur.

#### Sınıfın Kendisi Bir Yönergedir

| Sınıf | Anlamı | Aracıya söylediği |
|---|---|---|
| 1xx | Bilgilendirme | Ara yanıt; iletilir, saklanmaz |
| 2xx | Başarı | Yanıt çoğunlukla saklamaya uygundur |
| 3xx | Yönlendirme | İstemcinin bir adım daha atması gerekir |
| 4xx | İstemci hatası | Aynı istek çoğunlukla aynı sonucu verir, yeniden denemek boşunadır |
| 5xx | Sunucu hatası | Yöntem etkisizse istek yeniden denenebilir |

Sağ sütun bir kestirmedir, tanım değil: protokol saklanabilirliği tek tek kodlara bağlar
(`200`, `203`, `204`, `206`, `300`, `301`, `308`, `404`, `405`, `410`, `414`, `501`), üçüncü
sınıfın kimi üyesi adres değil saklanan kopya gösterir (`304`) ve dördüncü sınıfın kimi üyesi
yeniden denemeye açıktır (`408`, `429`). Tablonun kritik özelliği, sağ sütunun **kodun
kendisini değil sınıfını** kullanmasıdır.
Aracı 404 ile 400 arasındaki farkı bilmek zorunda değildir; ikisi de dördüncü sınıftandır ve
ikisinde de yeniden deneme boşunadır. Uygulamayı bilmeyen bir katmanın bilebileceği en üst
ayrıntı düzeyi budur ve protokol o düzeyi ilk basamağa yerleştirmiştir.

Birinci sınıf, kurgunun ölçüm kümesine girmez ve bunun bir nedeni vardır: 1xx bir **ara
yanıttır**, alışverişi bitirmez. Aracı onu ne saklar ne de üzerine karar kurar; olduğu gibi
iletir ve nihai yanıtı beklemeyi sürdürür. Ölçülen şey alışveriş başına alınan karar
olduğuna göre, alışverişi bitirmeyen bir yanıt sayıma giremez.

Web API Tasarımı kursunun kaynak tasarımı konusu **hangi kodun seçilmesi gerektiğini**
ölçer: gevşek bir eşlemenin istek sayısını nasıl artırdığını sayar. Burada seçim verilidir;
ölçülen şey, verilmiş bir sınıfın aracıya **kaç kararı aldırdığıdır**.

#### Üç Karar

Önceki dersin beş kararı yerinde kalır. Durum satırı üç karar daha açar:

| Karar | Gerçeği belirleyen |
|---|---|
| `saklamaya_uygun` | Yanıt gerçekten başarılı mı |
| `adres_degisti` | Kaynak gerçekten başka bir adrese mi taşındı |
| `yeniden_denenebilir` | Sunucu tarafında mı başarısız oldu ve yöntem etkisiz mi |

Kâhinin gerçeği bunları yanıtın **gerçek sınıfından** okur. Sunucunun bildirdiği kod ise ayrı
bir alandır ve ikisi her zaman uyuşmaz: kurguda birkaç sunucu hatası 200 diye bildirilir.
Bu, kursun her derste hatırlanan okumasının bu dersteki biçimidir — iletide yazanı
doğrulayan hiçbir şey yoktur.

Karar kümesi kırk alışveriş ve sekiz karardan, yani üç yüz yirmi karardan oluşur. Ölçülebilen
en küçük fark $1/320 = 0{,}0031$'dir; bundan küçük bir fark iddia edilmez.

Ölçümün varsayımları:

- **HA7** — Önceki dersin kırk alışverişi ve beş kararı olduğu gibi alınır; üzerine ayrı bir
  üreteçle durum kodu atanır ve ilk beş kararın gerçeği değişmez.
- **HA8** — Durum kodları sabit bir listeden çekilir ve birinci sınıf kümeye girmez: ara
  yanıt alışverişi bitirmez, bitmeyen alışveriş de sayıma giremez.
- **HA9** — Kâhinin gerçeği yanıtın **gerçek sınıfıdır**; sunucunun bildirdiği kod ayrı bir
  alandır ve kurguda kimi sunucu hatası 200 diye bildirilir. Bildirimi doğrulayan bir düzenek
  yoktur.
- **HA10** — İki rejim karşılaştırılır: aracı ya yalnız durum satırını okur, ya gövdeyi de
  açar. Aradaki tek fark sınıfın nereden okunduğudur — kodun ilk basamağından ya da gövdeden.
- **HA11** — Gövde, çözülmüş bir yanıt içeriğini temsil eden sabit yapıda bir alandır ve
  gerçek sınıfı doğru taşır; gövdeyi açmanın baytı bu alanın sayımından gelir.
- **HA12** — Tur bilançosu bir zamanlama ölçümü değil bir sayımdır: üçüncü sınıf yanıtın
  eklediği ve dördüncü sınıf yanıtın sildiği turlar alışveriş sayısından hesaplanır.

#### Çekirdeğin Genişletilmesi

Çekirdek önceki dersinkiyle aynıdır; üzerine `durum_ata` işlevi, üç yeni karar ve gövdeyi
gören ikinci bir görünürlük demeti eklenir. `kahin` işlevinin ilk beş satırına
dokunulmamıştır.

```python
TOHUM = 20260809
GUVENLI = {"GET", "HEAD"}
ETKISIZ = {"GET", "HEAD", "PUT", "DELETE"}
YONTEMLER = ["GET", "GET", "GET", "HEAD", "POST", "PUT", "DELETE"]
YOLLAR = ["/olcum/kuzey", "/olcum/yamac", "/ozet", "/kayit", "/oturum"]
DURUMLAR = [200, 200, 200, 201, 204, 301, 400, 404, 500, 503, 503]
TEMEL = ("saklanabilir", "paylasilabilir", "taze", "yinelenebilir",
         "yonlendirilebilir")
DURUM_KARARLARI = ("saklamaya_uygun", "adres_degisti", "yeniden_denenebilir")
KARARLAR = TEMEL + DURUM_KARARLARI
ACIK = ("yontem", "yol", "konak", "ozel_imi", "dogrulayici")
SATIR = ACIK + ("durum",)
SATIR_GOVDE = SATIR + ("govde",)


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def alisverisler(sayi: int = 40, tohum: int = TOHUM) -> list[dict]:
    r, liste = uretec(tohum), []
    for i in range(sayi):
        yontem, yol = YONTEMLER[r(7)], YOLLAR[r(5)]
        ozel = yol == "/oturum" or r(5) == 0
        degisti = r(3) == 0
        liste.append({
            "no": i + 1, "yontem": yontem, "yol": yol,
            "ozel": ozel, "degisti": degisti,
            "ozel_imi": ozel and r(4) != 0,
            "damga_atladi": degisti and r(6) == 0,
        })
    return liste


def durum_ata(liste: list[dict], tohum: int = TOHUM + 101) -> list[dict]:
    r = uretec(tohum)
    for a in liste:
        kod = DURUMLAR[r(11)]
        gercek = kod // 100
        yanilti = gercek == 5 and r(3) == 0   # sunucu, hatayı 200 diye bildirdi
        a["gercek_sinif"] = gercek
        a["durum"] = 200 if yanilti else kod
        a["yanilti"] = yanilti
    return liste


def kahin(a: dict) -> dict:
    return {
        "saklanabilir": a["yontem"] in GUVENLI and not a["ozel"],
        "paylasilabilir": not a["ozel"],
        "taze": not a["degisti"],
        "yinelenebilir": a["yontem"] in ETKISIZ,
        "yonlendirilebilir": True,
        "saklamaya_uygun": a["gercek_sinif"] == 2,
        "adres_degisti": a["gercek_sinif"] == 3,
        "yeniden_denenebilir": a["gercek_sinif"] == 5 and a["yontem"] in ETKISIZ,
    }


def ileti(a: dict, gorunur: tuple) -> dict:
    tam = {"yontem": a["yontem"], "yol": a["yol"], "konak": "istasyon.ornek",
           "ozel_imi": a["ozel_imi"],
           "dogrulayici": a["degisti"] and not a["damga_atladi"],
           "durum": a["durum"],
           "govde": {"sinif": a["gercek_sinif"], "yol": a["yol"],
                     "ayrinti": "cozumlenmis yanit govdesi",
                     "izleme": f"iz-{a['no']:04d}"}}
    return {k: v for k, v in tam.items() if k in gorunur}


def bayt(i: dict) -> int:
    return sum(len(k) + len(str(v)) + 4 for k, v in i.items())


def araci(i: dict) -> dict:
    k = {}
    if "yontem" in i:
        k["yinelenebilir"] = i["yontem"] in ETKISIZ
    if "ozel_imi" in i:
        k["paylasilabilir"] = not i["ozel_imi"]
        if "yontem" in i:
            k["saklanabilir"] = i["yontem"] in GUVENLI and not i["ozel_imi"]
    if "dogrulayici" in i:
        k["taze"] = not i["dogrulayici"]
    if "konak" in i:
        k["yonlendirilebilir"] = True
    if "durum" in i:
        sinif = i["govde"]["sinif"] if "govde" in i else i["durum"] // 100
        k["saklamaya_uygun"] = sinif == 2
        k["adres_degisti"] = sinif == 3
        if "yontem" in i:
            k["yeniden_denenebilir"] = sinif == 5 and i["yontem"] in ETKISIZ
    return k


def olc(alis: list[dict], gorunur: tuple, kararlar: tuple = KARARLAR) -> tuple:
    d = y = e = b = 0
    for x in alis:
        gercek, verilen = kahin(x), araci(ileti(x, gorunur))
        for karar in kararlar:
            if karar not in verilen:
                e += 1
            elif verilen[karar] == gercek[karar]:
                d += 1
            else:
                y += 1
        b += bayt(ileti(x, gorunur))
    return d, y, e, b


def dagilim(alis: list[dict], alan: str) -> dict:
    d: dict = {}
    for x in alis:
        d[x[alan]] = d.get(x[alan], 0) + 1
    return dict(sorted(d.items()))
```

`araci` işlevindeki tek satır bütün ayrımı taşır: gövde görünürse sınıf gövdeden, görünmezse
`durum // 100` ile satırdan okunur. İki aracı arasındaki fark yalnız budur.

#### İki Aracı

```python
alis = durum_ata(alisverisler())
print("gerçek sınıf dağılımı:", dagilim(alis, "gercek_sinif"))
print("bildirilen kod dağılımı:", dagilim(alis, "durum"))
print("sınıfını yanlış bildiren yanıt:", sum(x["yanilti"] for x in alis))
t = olc(alis, SATIR, TEMEL)
print(f"temel beş karar (iki rejimde de aynı): doğru {t[0]}, yanlış {t[1]}")
print()
print(f"{'rejim':<12s} {'doğru':>6s} {'yanlış':>7s} {'alınamayan':>11s} {'bayt':>6s}")
for ad, g in (("satır", SATIR), ("satır+gövde", SATIR_GOVDE)):
    d, y, e, b = olc(alis, g)
    print(f"{ad:<12s} {d:6d} {y:7d} {e:11d} {b:6d}")
```

```
gerçek sınıf dağılımı: {2: 22, 3: 3, 4: 7, 5: 8}
bildirilen kod dağılımı: {200: 16, 201: 5, 204: 4, 301: 3, 400: 4, 404: 3, 500: 1, 503: 4}
sınıfını yanlış bildiren yanıt: 3
temel beş karar (iki rejimde de aynı): doğru 195, yanlış 5

rejim         doğru  yanlış  alınamayan   bayt
satır           309      11           0   4004
satır+gövde     315       5           0   8044
```

İlk okunacak sütun **alınamayan**dır ve iki rejimde de sıfırdır. Durum satırı görünür
olduğu sürece aracı üç yeni kararın üçünü de alır; sunucuya sormaz, tur borçlanmaz. Önceki
dersin beş kararı da yerinde durur: iki rejimde de 195 doğru, 5 yanlış.

Aradaki bütün fark üç yüz yirmi kararın **altısındadır**. Satır rejimi 309, gövdeyi de açan
rejim 315 doğru karar verir. Buna karşılık gövdeyi açmak baytı 4004'ten 8044'e çıkarır.

#### Gövdenin Kazandırdığı

Altı kararın nereden geldiğine bakmak gerekir.

```python
print(f"{'':<20s} {'satır':>14s} {'satır+gövde':>16s}")
print(f"{'karar':<20s} {'doğru':>6s} {'yanlış':>7s} {'doğru':>8s} {'yanlış':>7s}")
for karar in DURUM_KARARLARI:
    s = [0, 0, 0, 0]
    for x in alis:
        gercek = kahin(x)
        for j, g in enumerate((SATIR, SATIR_GOVDE)):
            v = araci(ileti(x, g))
            s[2 * j + (0 if v[karar] == gercek[karar] else 1)] += 1
    print(f"{karar:<20s} {s[0]:6d} {s[1]:7d} {s[2]:8d} {s[3]:7d}")

print()
print(f"{'no':>3s} {'yöntem':<7s} {'yol':<14s} {'gerçek':>7s} {'bildirilen':>11s} "
      f"{'etkisiz':>8s}")
for x in alis:
    if x["yanilti"]:
        print(f"{x['no']:3d} {x['yontem']:<7s} {x['yol']:<14s} {x['gercek_sinif']:7d} "
              f"{x['durum']:11d} {str(x['yontem'] in ETKISIZ):>8s}")

ds, dg = olc(alis, SATIR), olc(alis, SATIR_GOVDE)
kazanc, bedel = dg[0] - ds[0], dg[3] - ds[3]
print()
print(f"gövdenin kazandırdığı karar {kazanc}, ödettiği bayt {bedel}, "
      f"karar başına {bedel / kazanc:.1f} bayt")
print("karar kümesi iki rejimde ayrılan alışveriş:",
      sum(1 for x in alis if araci(ileti(x, SATIR)) != araci(ileti(x, SATIR_GOVDE))))

eklenen = sum(x["gercek_sinif"] == 3 for x in alis)
kacinilan = sum(x["gercek_sinif"] == 4 for x in alis)
print(f"yönlendirmenin eklediği tur {eklenen}, boşuna denemeden kaçınılan tur "
      f"{kacinilan}, kırk alışverişte net {eklenen - kacinilan}")
```

```
                              satır      satır+gövde
karar                 doğru  yanlış    doğru  yanlış
saklamaya_uygun          37       3       40       0
adres_degisti            40       0       40       0
yeniden_denenebilir      37       3       40       0

 no yöntem  yol             gerçek  bildirilen  etkisiz
  2 PUT     /olcum/yamac         5         200     True
  8 GET     /olcum/kuzey         5         200     True
  9 GET     /ozet                5         200     True

gövdenin kazandırdığı karar 6, ödettiği bayt 4040, karar başına 673.3 bayt
karar kümesi iki rejimde ayrılan alışveriş: 3
yönlendirmenin eklediği tur 3, boşuna denemeden kaçınılan tur 7, kırk alışverişte net -4
```

Kırk alışverişin **otuz yedisinde** iki aracı aynı karar kümesini üretir. Gövdeyi açmak bu
otuz yedide tek bir kararı bile değiştirmez; yalnız bayt okur. Ayrılan üç alışverişte
kazanılan karar sayısı altı, ödenen bayt 4040 — karar başına 673,3 bayt.

Karşılaştırma ölçüsü de burada belirir. Durum satırı, aracıya kırk alışverişin kırkında üç
kararı birden verir ve bunun için alışveriş başına yalnız on iki bayt okutur. Gövde, üç yüz
yirmi kararın **altısını** düzeltir ve bunun için alışveriş başına yüz bir bayt daha okutur.
Sınıfın ilk basamağa konması bir yer kazanma tercihi değildir; aracının okuması gereken en
küçük veriyi yanıtın en başına koyma tercihidir.

#### Karar Bedava, İşlem Bir Tur

`adres_degisti` kararı iki rejimde de kırk kararın kırkında doğrudur; dersin en ucuz kararı
budur. Ucuzluk, kararın **sonucunun** da ucuz olduğu anlamına gelmez.

Üçüncü sınıf bir yanıt istenen içeriği vermez, yalnız başka bir adres bildirir. Aracı bunu
iletiden okur ve kimseye sormaz — karar fazladan tur istemez. Ama kararın gereğini yapmak,
yani yeni adrese gitmek, tam bir gidiş–dönüştür. Kırk alışverişin **üçü** üçüncü
sınıftandır ve bu üç alışveriş hiçbir başlık alanının kaldıramayacağı üç tur ekler; turu
kaldırmanın tek yolu adresi değiştirmektir.

Dördüncü sınıf ters yönde çalışır. Aracı bir 4xx gördüğünde aynı isteğin yeniden
gönderilmesinin çoğunlukla aynı sonucu vereceğini bilir ve göndermez. Kırk alışverişin **yedisi**
dördüncü sınıftandır; bu yedi alışverişte tek bir basamak, sonucu değişmeyecek bir turu
peşinen atmaktan kurtarır. Kırk alışverişteki bilanço **eksi dörttür**: yönlendirme üç tur
ekler, istemci hatası yedi turu siler.

Beşinci sınıfın bilançosu yönteme bağlıdır ve bu yüzden tek başına yazılamaz. Sekiz
alışverişin kaçının yeniden denenebileceğini belirleyen şey durum sınıfı değil, önceki
dersin ölçtüğü yöntem alanıdır; karar iki alanın kesişiminde durur.

Bu üç satır, ölçünün neden alan sayısı değil tur olduğunu gösterir. Durum satırı tek bir
alandır ve gövdeye hiç dokunmadan kırk alışverişte üç tur ekler, yedi tur siler.

#### Sınıfını Yanlış Bildiren Yanıt

Üç alışverişte sunucu, sunucu tarafında oluşan bir hatayı 200 diye bildirmiştir. Gövdede
gerçek durum yazılıdır; satırda yazmaz.

Satır rejiminde bu üç yanıt iki kararı birden bozar. `saklamaya_uygun` üçünde de yanlış
çıkar: aracı başarısız bir yanıtı önbelleğine koyar ve aynı hatayı sonraki isteklere de
dağıtır. `yeniden_denenebilir` de üçünde yanlış çıkar, çünkü üçünün de yöntemi etkisizdir
ve gerçek sınıfı bilinseydi istek yeniden gönderilebilirdi. `adres_degisti` bozulmaz: yanlış
bildirim üçüncü sınıfa dokunmaz.

Buradaki asıl sonuç şudur: **aracı yanlış karar vermedi, yanlış bildirime doğru davrandı.**
İlk basamağı okumak, ilk basamağın doğru yazıldığı varsayımıyla birlikte gelir. Protokolde
bu varsayımı sınayan bir düzenek yoktur; gövdeyi açmak da bir çözüm değildir, çünkü gövdeyi
yazan da aynı sunucudur.

Daraltma tarafı da vardır. Sunucu durum satırını doğru yazdığında aracı hiçbir şey
kaybetmez; yanlış yazdığında ise kaybı gövdeyle kapatmak alışveriş başına yüz bayttan fazla
tutar ve kırk alışverişin otuz yedisinde bu bayt boşa gider. Sınıfı doğru bildirmek,
aracının okuması gereken veriyi en aza indiren tek şeydir.

#### Özet

- Durum satırının ilk basamağı bir durum sınıfı verir; aracı saklama, yönlendirme ve yeniden
  deneme kararlarını kodun tamamından değil bu tek basamaktan çıkarır.
- Sınıf tabanlı okuma, aracının kodun ayrıntısını bilmesini gerektirmez: 400 ile 404 aynı
  yönergeyi taşır.
- Kırk alışveriş ve sekiz kararlık kümede satır rejimi 309 doğru, 11 yanlış karar ve 4004
  bayt; gövdeyi de açan rejim 315 doğru, 5 yanlış karar ve 8044 bayt verir.
- İki rejim kırk alışverişin otuz yedisinde aynı karar kümesini üretir; gövdenin kazandırdığı
  altı kararın bedeli karar başına 673,3 bayttır.
- Kararın ucuz olması işlemin de ucuz olduğunu göstermez: yönlendirme kırk alışverişte üç
  tur ekler, istemci hatası yedi turu siler, bilanço eksi dörttür.
- Sınıfını yanlış bildiren üç yanıt `saklamaya_uygun` ve `yeniden_denenebilir` kararlarını
  bozar; aracı yanlış karar vermez, yanlış bildirime doğru davranır.

#### Sonraki Adım

Durum satırı yanıtın **ne olduğunu** söyler. Söylemediği bir şey vardır: aynı adresin
birden çok karşılığı olabilir ve sunucu bunlardan hangisini gönderdiğini durum satırında
bildirmez. Aynı ölçüm kaydı ayrı biçimlerde, ayrı dillerde, ayrı kodlamalarda sunulabilir;
ikisi de 200 ile döner. Sonraki ders bu seçimin yapıldığı yeri, yani başlık alanlarını ölçer
ve iki soruyu birden sorar: ilgisiz alanlar eklemek aracıya karar kazandırır mı ve seçimin
hangi alana bakılarak yapıldığı bildirilmezse aracı hangi kopyayı kime verir.

### Başlıklar ve İçerik Pazarlığı

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/basliklar-ve-icerik-pazarligi

Alan eklemek karar kazandırmaz: üç yeni başlık baytı yüzde 84 artırıp sıfır karar getirirken, pazarlığa giren alanın bildirilmesi dokuz yanlış kopyayı düzeltir.

Önceki ders yanıtın ilk satırını ölçtü ve bir şeyi söylemeden bıraktı: durum satırı yanıtın
**ne olduğunu** söyler, hangi karşılığının gönderildiğini söylemez. Aynı ölçüm kaydı ayrı
biçimlerde, ayrı dillerde, ayrı sıkıştırmalarla sunulabilir ve hepsi 200 ile döner.

Bu seçimin yapıldığı yer başlık alanlarıdır. Bu ders iki soruyu birden sorar. Birincisi:
iletiye alan eklemek aracıya karar kazandırır mı. İkincisi: aynı adresin birden çok
karşılığı varken, seçimin **hangi alana bakılarak** yapıldığı bildirilmezse aracı hangi
kopyayı kime verir.

#### İki Tür Başlık Alanı

Başlık alanı, iki nokta ile ayrılmış bir ad ve bir değerdir. Aşağıdaki döküm öğretim
amaçlıdır ve çalıştırılmaz:

```text
GET /olcum/kuzey HTTP/1.1
Host: istasyon.ornek
Accept: application/json
Accept-Language: tr
X-Izleme-Kimligi: iz-0004
```

Aracı bakımından bu beş satır **iki kümeye** ayrılır. `Host` aracının yönlendirme kararını
kurar; `Accept` ailesi sunucunun hangi karşılığı üreteceğini belirler. Son satır ise
aracının çözemediği bir alandır: adını okur, değerini okur, ama ondan hiçbir karar
çıkaramaz.

Bu ayrım kursun ölçüsünün doğrudan karşılığıdır. **Aracının çözemediği alan, aracı için
bayttan başka bir şey değildir.** Bu cümle bir sezgidir; bu dersin ilk ölçümü onu sayıya
çevirir.

#### Gösterim ve Pazarlık

Bir adres bir **kaynağı** adlandırır; kaynağın ağ üzerinde taşınan karşılığına **gösterim
(representation)** denir. Aynı kaynağın birden çok gösterimi olabilir: aynı ölçüm kaydı
yapılandırılmış bir biçimde de, virgülle ayrılmış satırlar hâlinde de, düz metin olarak da
verilebilir.

İstemci hangisini istediğini `Accept` ailesindeki alanlarla bildirir; sunucu bu bildirime
bakarak bir gösterim seçer. Bu seçime **içerik pazarlığı (content negotiation)** denir. Üç
ekseni vardır ve üçü de aynı düzeneği kullanır:

| Eksen | İstemcinin bildirimi | Seçilen |
|---|---|---|
| Biçim | `Accept` | Ortam türü (media type) |
| Dil | `Accept-Language` | Metnin dili |
| Kodlama | `Accept-Encoding` | Aktarımdaki sıkıştırma |

Pazarlığın aracı için doğurduğu sonuç şudur: **tek bir adres artık tek bir yanıta karşılık
gelmez.** Aracı bir kopyayı sakladığında, o kopyanın hangi istek için üretildiğini de
bilmek zorundadır. Bilmezse, sakladığı kopyayı yanlış istemciye verir.

#### İsteyenin Bildirimi, Verenin Bildirimi

Pazarlık iki bildirimle yürür ve ikisi ayrı yönlerdedir. İstek tarafındaki `Accept` ailesi
istemcinin **kabul edebileceklerini** sayar; bir tercih listesidir, tek bir seçim değildir.
Yanıt tarafındaki `Content-Type` ise sunucunun **gerçekten gönderdiğini** bildirir ve
tektir.

Aracının ikisine de ihtiyacı vardır, ayrı nedenlerle. İstek tarafındaki bildirim, elindeki
kopyanın bu isteğe uyup uymadığını sınamasını sağlar. Yanıt tarafındaki bildirim, sakladığı
kopyanın ne olduğunu bilmesini sağlar. Biri olmadan öbürü işe yaramaz: istemcinin ne
istediğini bilmek, elde ne olduğu bilinmeden bir eşleşme kurmaz.

İkisinin yanılma biçimi de ayrıdır. `Accept` bir tercih listesi olduğu için sunucu
listedeki hiçbir seçeneği üretemeyebilir; o durumda pazarlık sonuçsuz kalır ve karar
istemciye döner. `Content-Type` ise tek bir değer olduğu için yanlış yazılabilir: sunucu bir
biçim gönderip başka bir biçim bildirdiğinde aracı yanlış bildirime göre saklar ve kopyayı
yanlış anahtara bağlar.

Kodlama ekseni bu ikisinden bir noktada ayrılır. Biçim ve dil, gösterimin **anlamına**
dokunur: başka biçim başka bir kayıt düzeni, başka dil başka bir metindir. Kodlama ise
anlamı değiştirmez, yalnız aktarımdaki temsili değiştirir — sıkıştırılmış kayıt açıldığında
sıkıştırılmamışıyla aynıdır. Aracı bakımından fark yine de yok sayılamaz: sıkıştırılmış bir
kopyayı, sıkıştırmayı kabul ettiğini bildirmemiş bir istemciye vermek o istemci için
okunamayan bir yanıt üretir. Anlam aynı kalsa da anahtar ayrı tutulmak zorundadır.

İki ölçümün varsayımları:

- **HA13** — Aynı kırk alışveriş ve aynı beş karar kullanılır; üzerine ayrı bir üreteçle her
  alışverişin istediği biçim ve dil atanır. `kahin` ve `araci` işlevlerine dokunulmaz.
- **HA14** — Eklenen üç alan gerçek bilgi taşır ama aracının kodunda hiçbir dala girmez;
  ölçülen şey alanın büyüklüğü değil, okuyanın onunla bir dal kurup kuramadığıdır.
- **HA15** — İkinci ölçüm yalnız **saklanabilir** alışverişler üzerinde yürür: güvenli
  yöntemli ve kişiye özel olmayanlar. Kırk alışverişin geri kalanı aracının deposuna girmez.
- **HA16** — Aracı anahtar başına tek bir kopya saklar ve anahtara adresin yanında yalnız
  **bildirilen** alanlar girer; ilk gelen kopya yerini tutar ve sonraki isteklere o verilir.
- **HA17** — İstemcinin istediği gösterim bilinir çünkü onu da biz ürettik; kopyanın doğru mu
  yanlış mı verildiği bu bilinen istekle karşılaştırılarak sayılır.
- **HA18** — Pazarlık imi üç değerde denenir: hiç bildirim, yalnız biçim, biçim ve dil.
  Bildirimin doğruluğunu sınayan bir düzenek yoktur; aracı sunucunun saydığı alanlara inanır.

#### Birinci Ölçüm: Alan Eklemek

İlk ölçüm, önceki iki dersin kurduğu beş kararlık kümeyi olduğu gibi alır ve tek bir şeyi
değiştirir: iletiye aracının çözemediği üç alan eklenir. `kahin` ve `araci` işlevlerine
dokunulmaz.

```python
TOHUM = 20260809
GUVENLI = {"GET", "HEAD"}
ETKISIZ = {"GET", "HEAD", "PUT", "DELETE"}
YONTEMLER = ["GET", "GET", "GET", "HEAD", "POST", "PUT", "DELETE"]
YOLLAR = ["/olcum/kuzey", "/olcum/yamac", "/ozet", "/kayit", "/oturum"]
BICIMLER = ["json", "json", "csv", "metin", "json"]
DILLER = ["tr", "tr", "en", "tr", "en"]
KARARLAR = ("saklanabilir", "paylasilabilir", "taze", "yinelenebilir",
            "yonlendirilebilir")
GORUNUR = {"açık": ("yontem", "yol", "konak", "ozel_imi", "dogrulayici")}
GORUNUR["açık+ek"] = GORUNUR["açık"] + ("izleme_kimligi", "istemci_surumu", "bolge")


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def alisverisler(sayi: int = 40, tohum: int = TOHUM) -> list[dict]:
    r, liste = uretec(tohum), []
    for i in range(sayi):
        yontem, yol = YONTEMLER[r(7)], YOLLAR[r(5)]
        ozel = yol == "/oturum" or r(5) == 0
        degisti = r(3) == 0
        liste.append({
            "no": i + 1, "yontem": yontem, "yol": yol,
            "ozel": ozel, "degisti": degisti,
            "ozel_imi": ozel and r(4) != 0,
            "damga_atladi": degisti and r(6) == 0,
        })
    return liste


def tercih_ata(liste: list[dict], tohum: int = TOHUM + 211) -> list[dict]:
    r = uretec(tohum)
    for a in liste:
        a["bicim"], a["dil"] = BICIMLER[r(5)], DILLER[r(5)]
    return liste


def kahin(a: dict) -> dict:
    return {
        "saklanabilir": a["yontem"] in GUVENLI and not a["ozel"],
        "paylasilabilir": not a["ozel"],
        "taze": not a["degisti"],
        "yinelenebilir": a["yontem"] in ETKISIZ,
        "yonlendirilebilir": True,
    }


def ileti(a: dict, gorunur: tuple) -> dict:
    tam = {"yontem": a["yontem"], "yol": a["yol"], "konak": "istasyon.ornek",
           "ozel_imi": a["ozel_imi"],
           "dogrulayici": a["degisti"] and not a["damga_atladi"],
           "izleme_kimligi": f"iz-{a['no']:04d}", "istemci_surumu": "olcum-alici",
           "bolge": "kuzey-yamac"}
    return {k: v for k, v in tam.items() if k in gorunur}


def bayt(i: dict) -> int:
    return sum(len(k) + len(str(v)) + 4 for k, v in i.items())


def araci(i: dict) -> dict:
    k = {}
    if "yontem" in i:
        k["yinelenebilir"] = i["yontem"] in ETKISIZ
    if "ozel_imi" in i:
        k["paylasilabilir"] = not i["ozel_imi"]
        if "yontem" in i:
            k["saklanabilir"] = i["yontem"] in GUVENLI and not i["ozel_imi"]
    if "dogrulayici" in i:
        k["taze"] = not i["dogrulayici"]
    if "konak" in i:
        k["yonlendirilebilir"] = True
    return k


def olc(ad: str, alis: list[dict]) -> tuple:
    d = y = e = t = b = 0
    for a in alis:
        gercek, verilen = kahin(a), araci(ileti(a, GORUNUR[ad]))
        eksik = False
        for karar in KARARLAR:
            if karar not in verilen:
                e += 1
                eksik = True
            elif verilen[karar] == gercek[karar]:
                d += 1
            else:
                y += 1
        t += 1 if eksik else 0
        b += bayt(ileti(a, GORUNUR[ad]))
    return d, y, e, t, b


def pazarlik(alis: list[dict], im: tuple) -> tuple:
    """Adres başına kopya saklayan aracı; anahtara yalnız bildirilen alanlar girer."""
    depo, dogru, yanlis, imbayt = {}, 0, 0, 0
    for a in alis:
        if not (a["yontem"] in GUVENLI and not a["ozel"]):
            continue
        imbayt += bayt({"pazarlik_imi": im}) if im else 0
        anahtar = (a["yol"],) + tuple(a[alan] for alan in im)
        istenen = (a["bicim"], a["dil"])
        verilen = depo.setdefault(anahtar, istenen)
        if verilen == istenen:
            dogru += 1
        else:
            yanlis += 1
    return dogru, yanlis, len(depo), imbayt
```

Eklenen üç alan bir izleme kimliği, bir istemci sürümü ve bir bölge adıdır. Üçü de gerçek
bilgi taşır ve üçü de `araci` işlevinde hiçbir dala girmez.

```python
alis = tercih_ata(alisverisler())
print(f"{'görünürlük':<10s} {'doğru':>6s} {'yanlış':>7s} {'alınamayan':>11s} "
      f"{'fazladan tur':>13s} {'bayt':>6s}")
for ad in ("açık", "açık+ek"):
    d, y, e, t, b = olc(ad, alis)
    print(f"{ad:<10s} {d:6d} {y:7d} {e:11d} {t:13d} {b:6d}")
ba, be = olc("açık", alis)[4], olc("açık+ek", alis)[4]
print(f"alan sayısı {len(GORUNUR['açık'])} -> {len(GORUNUR['açık+ek'])}, "
      f"bayt {ba} -> {be}, artış %{100 * (be - ba) / ba:.0f}, kazanılan karar "
      f"{olc('açık+ek', alis)[0] - olc('açık', alis)[0]}")
```

```
görünürlük  doğru  yanlış  alınamayan  fazladan tur   bayt
açık          195       5           0             0   3524
açık+ek       195       5           0             0   6484
alan sayısı 5 -> 8, bayt 3524 -> 6484, artış %84, kazanılan karar 0
```

Karar tablosunun dört sütunu da aynı kalır: 195 doğru, 5 yanlış, 0 alınamayan, 0 fazladan
tur. Bayt ise 3524'ten 6484'e çıkar — **yüzde 84 artış, sıfır karar.** Kırk alışverişte iki
bin dokuz yüz altmış bayt fazladan taşınmış ve karşılığında hiçbir şey alınmamıştır.

Bu, kursun ikinci iddiasıdır ve ölçüsü burada ödenir: **alan eklemek karar kazandırmaz.**
Bir alanın değeri, taşıdığı bilgiden değil, okuyanın o bilgiyle bir dal kurup
kuramadığından gelir. İzleme kimliği ölçüm istasyonu için değerli olabilir; aracı için
kırk alışverişte bin bayttır ve daha fazlası değildir.

#### İkinci Ölçüm: Pazarlığın Bedeli

İkinci ölçüm ters yöndedir. Şimdi eklenen alan aracının **çözebildiği** bir alandır ve
sorusu şudur: pazarlığa hangi alanın girdiğini bildirmek kaç karar kazandırır.

Aracı, sakladığı kopyayı bir anahtara bağlar. Bildirim yoksa anahtar yalnız adrestir: ilk
gelen kopya saklanır ve sonraki her isteğe o verilir. Bildirim varsa anahtara bildirilen
alanlar da girer ve her gösterim ayrı saklanır. Ölçüm, yalnız saklanabilir alışverişler
üzerinde yürür — yani güvenli yöntemli ve kişiye özel olmayanlar.

Bu bildirimin protokoldeki karşılığı `Vary` başlık alanıdır: sunucu, yanıtı üretirken
isteğin hangi başlık alanlarına baktığını burada sayar. Modeldeki `pazarlik_imi` demeti tam
olarak bu listedir. Alanın yanıt tarafında durması da rastlantı değildir — pazarlığa neyin
girdiğini yalnız seçimi yapan taraf bilir ve aracı ancak kendisine söylenirse bilir.

Listenin uzunluğunun bir sınırı vardır ve bu sınır ölçümün son sütununda görünür. Anahtara
giren her alan, saklanan kopya sayısını çarpar. Değeri her istemcide başka olan bir alan
listeye girerse her istek ayrı bir anahtar üretir ve aracı hiçbir kopyayı ikinci kez
kullanamaz. Doğru bildirim, pazarlığa gerçekten giren alanları sayan ve girmeyenleri
saymayan bildirimdir: eksiği yanlış kopya, fazlası hiç kullanılmayan kopya üretir.

```python
saklanan = [a for a in alis if a["yontem"] in GUVENLI and not a["ozel"]]
print(f"pazarlığa giren alışveriş {len(saklanan)}, "
      f"istenen gösterim {len({(a['bicim'], a['dil']) for a in saklanan})} çeşit, "
      f"adres {len({a['yol'] for a in saklanan})}")
print()
print(f"{'bildirilen im':<22s} {'doğru kopya':>12s} {'yanlış kopya':>13s} "
      f"{'saklanan':>9s} {'im baytı':>9s}")
for im in ((), ("bicim",), ("bicim", "dil")):
    d, y, n, ib = pazarlik(alis, im)
    print(f"{str(im):<22s} {d:12d} {y:13d} {n:9d} {ib:9d}")

d0, y0, n0, b0 = pazarlik(alis, ())
d2, y2, n2, b2 = pazarlik(alis, ("bicim", "dil"))
print()
print(f"tam im: kazanılan karar {d2 - d0}, ödenen bayt {b2 - b0}, "
      f"karar başına {(b2 - b0) / (d2 - d0):.1f} bayt; "
      f"saklanan kopya {n0} -> {n2}")
```

```
pazarlığa giren alışveriş 13, istenen gösterim 6 çeşit, adres 4

bildirilen im           doğru kopya  yanlış kopya  saklanan  im baytı
()                                4             9         4         0
('bicim',)                        9             4         8       338
('bicim', 'dil')                 13             0        11       416

tam im: kazanılan karar 9, ödenen bayt 416, karar başına 46.2 bayt; saklanan kopya 4 -> 11
```

On üç saklanabilir alışveriş, dört adres ve altı çeşit gösterim isteği vardır. Ölçüm bandının
alt sınırı bu kümede $1/13 = 0{,}077$'dir; bundan küçük bir fark iddia edilmez.

Hiçbir bildirim yokken aracı dört kopya saklar ve **on üç isteğin dokuzuna yanlış kopyayı
verir.** Yanlış kopya, bozuk bir yanıt değildir: geçerli, taze, doğru adresten gelmiş bir
gösterimdir — yalnız isteyene ait değildir. İsteyen düz metin beklerken yapılandırılmış bir
kayıt alır, Türkçe beklerken İngilizce alır.

Yalnız biçim bildirildiğinde yanlış kopya dokuzdan dörde iner. Kalan dördü dil eksenindendir:
aracı biçimi ayırmayı öğrenmiş, dili ayırmayı öğrenmemiştir. **Eksik bildirim, hiç bildirim
yapmamanın küçük bir sürümüdür**, çünkü kaçırılan tek bir eksen bile kopyayı yanlış kılmaya
yeter. İki eksen birden bildirildiğinde yanlış kopya sıfıra iner.

#### İki Ölçümün Yan Yana Okunması

İki ölçüm aynı iletiye alan ekliyor ve karşılıkları taban tabana zıt çıkıyor.

| Eklenen | Bayt | Kazanılan karar |
|---|---|---|
| Üç çözülemeyen alan | 2960 | 0 |
| Pazarlık imi | 416 | 9 |

Fark alanların büyüklüğünde değil, okuyanın onlarla ne yapabildiğindedir. Birinci ölçümdeki
üç alan aracının koduna hiçbir dal eklemez; ikinci ölçümdeki tek alan aracının anahtarını
değiştirir. Bu yüzden ölçü **alan sayısı** değil **karar** olmak zorundadır: alan sayısı
ikisini de "iletiye bir şey eklendi" diye sayardı.

Pazarlığın ikinci bir bedeli daha vardır ve bu bedel bayt değildir. Bildirim eksiksiz
olduğunda aracının sakladığı kopya sayısı dörtten **on bire** çıkar. Aynı adres artık aracı
için tek bir girdi değildir; her gösterim ayrı yer tutar ve ayrı tazelenir. Doğru kopyayı
vermenin karşılığı, aynı kaynağı birden çok kez saklamaktır.

Ve dördüncü okuma burada da geçerlidir. Aracı, pazarlığa gerçekten hangi alanların girdiğini
sınayamaz. Sunucu eksik bildirirse aracı eksik anahtarla saklar ve yanlış kopyayı **hiçbir
hata vermeden** dağıtır. Bildirimin doğruluğunu denetleyen bir düzenek yoktur; aracı burada
da bildirime inanır. Bu derste sayılan bütün kazanç ve bedeller, o tek varsayımın üzerinde
durur.

#### Özet

- Başlık alanları aracı bakımından ikiye ayrılır: çözülüp bir karara bağlananlar ve yalnız
  taşınanlar.
- Kırk alışverişlik kümede üç çözülemeyen alan eklendiğinde karar tablosu aynı kalır — 195
  doğru, 5 yanlış — ama bayt 3524'ten 6484'e, yüzde 84 artar.
- Bir adres bir kaynağı adlandırır; içerik pazarlığı aynı kaynağın biçim, dil ve kodlama
  eksenlerinde birden çok gösterimi olmasıdır.
- Pazarlığa giren alan bildirilmezse aracı on üç saklanabilir alışverişin dokuzunda yanlış
  kopyayı verir; yalnız biçim bildirilirse dört, iki eksen de bildirilirse sıfır yanlış
  kopya kalır.
- Aynı iletiye eklenen 2960 bayt sıfır karar, 416 bayt dokuz karar kazandırır; ölçünün alan
  sayısı değil karar olmasının nedeni budur.
- Doğru kopyayı vermenin bedeli baytla sınırlı değildir: saklanan kopya sayısı dörtten on
  bire çıkar.

#### Sonraki Adım

İçerik pazarlığı, aynı adresin **birden çok gösterimi** olması demektir ve aracı hangi
kopyayı kime vereceğini başlık alanlarından çıkarır. Bu dersin ölçtüğü bütün gösterimlerin
ortak bir özelliği vardı: aynı bildirimi yapan iki istemci aynı kopyayı hak ediyordu. Biçim
ve dil, isteyenin **kim olduğunu** değil, ne istediğini anlatır.

Peki yanıt **kişiye özelse** — yani aynı adrese, aynı biçim ve dil bildirimiyle gelen iki
istemcinin farklı yanıtları hak ettiği durumda ne olur? O zaman doğru kopyayı seçmek için
gereken alan, artık ne istendiğini değil kime ait olunduğunu taşımak zorundadır. Sonraki
ders bu alanı ve aracının onunla ilişkisini ele alacak.

### Çerezler ve Durum Taşıma

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/cerezler-ve-durum-tasima

Çerezin kapsam kuralı hangi iletilere iliştiğini belirler, im kuralı saklanan yanıtın kime gideceğini; kırk alışverişte üç kapsam kuralı sırasıyla 11, 24 ve 40 ileti taşır ve imsiz kalan iki alışveriş aracıya iki yanlış paylaşılabilirlik kararı aldırır.

Önceki ders içerik pazarlığını kurdu: aynı adres birden çok gösterime karşılık gelir ve
aracı hangi kopyayı kime vereceğini başlık alanlarından çıkarır. Pazarlık, gösterimi
isteğin **biçim tercihine** göre seçer — dil, kodlama, ortam türü. Bunların hepsi
isteğin kendisinde yazılıdır ve aracı hepsini okur.

Peki yanıt biçime değil **kişiye** özelse? O zaman seçimi yapan alan artık tercihi değil,
kimliği taşımak zorundadır. **Çerez (cookie)**, kişiye özelliğin kendisini taşıyan alandır.
Bu dersin sorusu çerezin ne işe yaradığı değil — aracının onu görünce ne bildiği ve
görmediğinde ne bilemediğidir.

#### Durumsuz Protokolde Durum

HTTP **durumsuz (stateless)** tanımlanır: her istek kendi başına anlaşılır olmalı, sunucu
önceki isteği hatırlamak zorunda kalmamalıdır. Çerez bu tanımı bozmaz, etrafından dolaşır.
Sunucu durumu kendinde tutmaz; **istemcide tutar** ve istemci onu her istekte geri gönderir.
Böylece her istek yine kendi başına anlaşılır kalır, çünkü durum artık iletinin içindedir.

Alışveriş iki alandan ibarettir. Sunucu yanıtta `Set-Cookie` alanıyla bir ad–değer çifti ve
ona bağlı nitelikleri bildirir; istemci sonraki isteklerde `Cookie` alanıyla ad–değer
çiftini geri gönderir. Nitelikler yanıtta gider, geri dönüşte gitmez: istemci yalnız çifti
yollar.

```text
# ogretilen dokum , calistirilmamistir

sunucu -> istemci
  HTTP/1.1 200 OK
  Set-Cookie: oturum=k7; Path=/oturum; Domain=istasyon.ornek; Max-Age=3600;
              HttpOnly; SameSite=Strict; Secure
  Cache-Control: private
  Vary: Cookie

istemci -> sunucu
  GET /oturum/ozet HTTP/1.1
  Host: istasyon.ornek
  Cookie: oturum=k7
```

Niteliklerin her biri bir soruya yanıt verir. `Domain` ve `Path` çerezin **kapsamını**
belirler: hangi konak ve hangi yol öneki için gönderileceğini. `Max-Age` ömrünü belirler;
verilmezse çerez oturumluktur ve istemci kapandığında düşer. `HttpOnly`, çerezi belge
içindeki betiklerin okumasına kapatır. `SameSite`, çerezin başka bir kaynaktan tetiklenen
isteklerde taşınıp taşınmayacağını belirler. `Secure`, çerezi taşımanın türüne bağlar.

Yanıt tarafındaki iki alan aracıyı doğrudan ilgilendirir. `Cache-Control: private`, yanıtın
paylaşılan bir önbelleğe konmaması gerektiğini bildirir. `Vary: Cookie`, saklanan kopyanın
hangi istek alanına bağlı olduğunu bildirir — aynı adresin `Cookie` alanına göre ayrı
kopyaları vardır. Ortak tanımın **kişiye özel imi** bu iki alanın taşıdığı bildirimdir.

Kimlik doğrulama akışının kendisi bu kursun konusu değildir; oturum açma, oturum yenileme
ve belirteç kullanımı **Kimlik Doğrulama ve Yetkilendirme** kursunda kuruldu. Burada çerez
bir kimlik düzeneği olarak değil, bir **kapsam kuralı** olarak ölçülür.

#### Çerezin Değeri Aracıya Ne Söyler

Çerez bir ad–değer çiftidir ve değerin anlamını yalnız sunucu bilir. `oturum=k7` dizgisi
aracı için bir dizgidir: kimin oturumu olduğunu, ne kadar yetki taşıdığını, hatta bir oturum
belirteci mi yoksa bir dil tercihi mi olduğunu söylemez. Aracı uygulamayı bilmez; çerezin
değerini çözmeye kalksaydı uygulamayı bilmiş olurdu.

Bunun doğrudan bir sonucu var: **aracı, çerezin varlığından yanıtın kişiye özel olduğunu
çıkaramaz.** Çıkarsaydı, yalnız bir dil tercihi taşıyan çerez yüzünden paylaşılabilir bir
yanıtı saklamaktan vazgeçerdi. Tersini de yapamaz: çerezin yokluğundan yanıtın
paylaşılabilir olduğunu çıkaramaz, çünkü sunucu kimliği başka bir alandan da okumuş olabilir.

Karar bu yüzden çerezde değil, **yanıttaki imdedir**. Sunucu ne bildirirse aracı onu bilir.
Ortak kurgunun kişiye özel imi tam olarak bu bildirimdir ve iletide görünen tek kişiye
özellik göstergesidir. Çerezin kendisi ölçüme bir karar eklemez; ölçüme giren şey çerezin
**hangi iletilerde bulunduğudur**, ne taşıdığı değil.

Ömür niteliği de aynı sınıfa girer. `Max-Age` verilmiş bir çerez ile oturumluk bir çerez
arasındaki fark istemcinin çerezi ne kadar saklayacağıdır; aracının o iletiden alacağı karar
ikisinde de aynıdır. Aracı çerezin ne zaman düşeceğini bilmez ve bilmesi gerekmez.

#### İki Ayrı Kural

Çerezle ilgili iki karar vardır ve karıştırılmaları en sık görülen kusurdur.

**Kapsam kuralı** isteği ilgilendirir: çerez **hangi iletilerde taşınır?** Bunu `Domain` ve
`Path` belirler ve kararı istemci verir. **İm kuralı** yanıtı ilgilendirir: saklanan yanıt
**kime gider?** Bunu `Cache-Control` ve `Vary` belirler ve kararı sunucu bildirir, aracı
uygular.

İkisi bağımsızdır. Kapsam doğru kurulmuş bir çerez, imi konmamış bir yanıtla birlikte
gidebilir; o zaman çerez doğru yere gitmiştir ama yanıt yanlış kişiye verilebilir. Tersi de
olur: im doğru konmuş bir yanıt, kapsamı gereğinden geniş bir çerezle birlikte gidebilir.

Ölçümün varsayımları:

- **HA41** — Kırk alışveriş ortak kurgudan üretilir; kâhin kurguyu biz ürettiğimiz için
  bilinir ve her alışverişte yanıtın gerçekten kişiye özel olup olmadığını söyler.
- **HA42** — Üç kapsam kuralı denenir: yalnız oturum yolu, ölçüm ve oturum yolu öneki,
  konağın tümü. Kapsam kuralı yalnız yola bakar; kural kâhini kullanmaz.
- **HA43** — Kişiye özel im, iletide görünen tek bildirimdir. Sunucu kimi kişiye özel
  yanıtta imi koymayı atlar; **iletide yazanı doğrulayan hiçbir şey yoktur.**
- **HA44** — Aracı imi gördüğünde `paylasilabilir` ve `saklanabilir` kararlarını fazladan
  gidiş–dönüş olmadan alır; görmediğinde ikisini de alamaz ve sunucuya bir tur daha atar.
- **HA45** — Bayt sayımı alan adı, değer ve ayraç uzunluğundan hesaplanır; ölçüm bir ağ
  ölçümü değil, ileti içeriğinin sayımıdır.

#### Ölçüm

```python
"""Cerez kapsami ve durum tasima: aracinin ozel imden aldigi karar.

Bolum 1 - kapsam kurali cerezi hangi iletilere ilistiriyor.
Bolum 2 - im alani gorunurken ve zarfa alinmisken karar tablosu.
"""
TOHUM = 20260809
GUVENLI = {"GET", "HEAD"}
ETKISIZ = {"GET", "HEAD", "PUT", "DELETE"}
YONTEMLER = ["GET", "GET", "GET", "HEAD", "POST", "PUT", "DELETE"]
YOLLAR = ["/olcum/kuzey", "/olcum/yamac", "/ozet", "/kayit", "/oturum"]
KARARLAR = ("saklanabilir", "paylasilabilir", "taze", "yinelenebilir",
            "yonlendirilebilir")


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def alisverisler(sayi=40):
    r, liste = uretec(TOHUM), []
    for i in range(sayi):
        yontem, yol = YONTEMLER[r(7)], YOLLAR[r(5)]
        ozel = yol == "/oturum" or r(5) == 0
        degisti = r(3) == 0
        liste.append({"no": i + 1, "yontem": yontem, "yol": yol,
                      "ozel": ozel, "degisti": degisti,
                      "ozel_imi": ozel and r(4) != 0,
                      "damga_atladi": degisti and r(6) == 0})
    return liste


def kahin(a):
    return {"saklanabilir": a["yontem"] in GUVENLI and not a["ozel"],
            "paylasilabilir": not a["ozel"], "taze": not a["degisti"],
            "yinelenebilir": a["yontem"] in ETKISIZ,
            "yonlendirilebilir": True}


def ileti(a, gorunur):
    tam = {"yontem": a["yontem"], "yol": a["yol"], "konak": "istasyon.ornek",
           "ozel_imi": a["ozel_imi"],
           "dogrulayici": a["degisti"] and not a["damga_atladi"]}
    return {k: v for k, v in tam.items() if k in gorunur}


def bayt(i):
    return sum(len(k) + len(str(v)) + 4 for k, v in i.items())


def araci(i):
    k = {}
    if "yontem" in i:
        k["yinelenebilir"] = i["yontem"] in ETKISIZ
    if "ozel_imi" in i:
        k["paylasilabilir"] = not i["ozel_imi"]
        if "yontem" in i:
            k["saklanabilir"] = i["yontem"] in GUVENLI and not i["ozel_imi"]
    if "dogrulayici" in i:
        k["taze"] = not i["dogrulayici"]
    if "konak" in i:
        k["yonlendirilebilir"] = True
    return k


GORUNUR = {"açık": ("yontem", "yol", "konak", "ozel_imi", "dogrulayici"),
           "im zarfta": ("yontem", "yol", "konak", "dogrulayici")}
KAPSAM = {"oturum yolu": lambda a: a["yol"] == "/oturum",
          "ölçüm öneki": lambda a: a["yol"].startswith(("/olcum", "/oturum")),
          "konağın tümü": lambda a: True}
A = alisverisler()

print(f"alışveriş {len(A)} | karar {len(A) * len(KARARLAR)} | kişiye özel "
      f"{sum(a['ozel'] for a in A)} | imsiz kalan "
      f"{sum(a['ozel'] and not a['ozel_imi'] for a in A)}")
print("imsiz kalan alışverişler:",
      [(a["no"], a["yontem"], a["yol"]) for a in A
       if a["ozel"] and not a["ozel_imi"]])
print()
print("kapsam kuralı  çerez taşıyan  özel ve taşıyan  özel ama taşımayan"
      "  gereksiz taşıyan")
for ad, kural in KAPSAM.items():
    print(f"  {ad:12s} {sum(kural(a) for a in A):11d}"
          f" {sum(kural(a) and a['ozel'] for a in A):16d}"
          f" {sum(not kural(a) and a['ozel'] for a in A):19d}"
          f" {sum(kural(a) and not a['ozel'] for a in A):17d}")
print()
print("karar              açık: doğru yanlış   im zarfta: doğru yanlış alınamayan")
for karar in KARARLAR:
    s = {}
    for ad in GORUNUR:
        d = y = e = 0
        for a in A:
            g, v = kahin(a), araci(ileti(a, GORUNUR[ad]))
            if karar not in v:
                e += 1
            elif v[karar] == g[karar]:
                d += 1
            else:
                y += 1
        s[ad] = (d, y, e)
    print(f"  {karar:18s} {s['açık'][0]:5d} {s['açık'][1]:6d}"
          f" {s['im zarfta'][0]:16d} {s['im zarfta'][1]:6d}"
          f" {s['im zarfta'][2]:10d}")
print()
print("görünürlük   doğru  yanlış  alınamayan  fazladan tur   bayt")
for ad in GORUNUR:
    d = y = e = t = b = 0
    for a in A:
        g, v = kahin(a), araci(ileti(a, GORUNUR[ad]))
        eksik = False
        for karar in KARARLAR:
            if karar not in v:
                e += 1
                eksik = True
            elif v[karar] == g[karar]:
                d += 1
            else:
                y += 1
        t += 1 if eksik else 0
        b += bayt(ileti(a, GORUNUR[ad]))
    print(f"  {ad:10s} {d:5d} {y:7d} {e:11d} {t:13d} {b:6d}")
```

```
alışveriş 40 | karar 200 | kişiye özel 17 | imsiz kalan 2
imsiz kalan alışverişler: [(1, 'POST', '/kayit'), (34, 'POST', '/kayit')]

kapsam kuralı  çerez taşıyan  özel ve taşıyan  özel ama taşımayan  gereksiz taşıyan
  oturum yolu           11               11                   6                 0
  ölçüm öneki           24               12                   5                12
  konağın tümü          40               17                   0                23

karar              açık: doğru yanlış   im zarfta: doğru yanlış alınamayan
  saklanabilir          40      0                0      0         40
  paylasilabilir        38      2                0      0         40
  taze                  37      3               37      3          0
  yinelenebilir         40      0               40      0          0
  yonlendirilebilir     40      0               40      0          0

görünürlük   doğru  yanlış  alınamayan  fazladan tur   bayt
  açık         195       5           0             0   3524
  im zarfta    117       3          80            40   2859
```

#### Kapsamın Ödettiği

Üst tablo üç kapsam kuralını yan yana koyuyor ve hiçbiri sorunsuz değil.

**Oturum yoluna daraltılmış** kural çerezi **11** iletide taşıyor ve bu on birin hepsi
gerçekten kişiye özel. Gereksiz taşıma **0**. Bedeli sağdaki sütunda: kişiye özel olan
**6** yanıt kapsamın dışında kalıyor. Kurgu bunu doğrudan söylüyor — kişiye özellik yolun
kendisinden gelmiyor, sunucu `/oturum` dışındaki kimi yanıtı da kişiye özel üretiyor.

**Konağın tümüne açılmış** kural bu altısını kapatıyor: kapsam dışında kalan kişiye özel
yanıt **0**. Karşılığında çerez **23** iletide gereksiz taşınıyor — kırk alışverişin yarıdan
fazlasında, hiçbir işe yaramadan. Aradaki **ölçüm öneki** kuralı ikisinin ortası: **12**
gereksiz taşıma ve kapsam dışında kalan **5** kişiye özel yanıt.

Örüntü şudur: **kapsam kuralı yola bakar, kişiye özellik ise yoldan çıkmaz.** Kuralı
genişletmek kapsam dışında kalanı azaltır ama gereksiz taşımayı doğrusal olarak büyütür.
İkisini birden sıfırlayan bir yol yoktur, çünkü kapsam kuralının bilebileceği tek şey
adrestir.

#### İmin Ödettiği

Alt tablolar ikinci kurala bakıyor. Kırk alışverişin **17**'sinde yanıt kişiye özel ve
sunucu bunların **2**'sinde imi koymayı atlamış. Aracı iki alışverişte de yanıtı
paylaşılabilir sanıyor: `paylasilabilir` satırında **38** doğru, **2** yanlış.

`saklanabilir` satırı **40** doğru veriyor ve bu bir tesadüf değil. İmsiz kalan iki
alışverişin ikisi de `POST /kayit`; ikisinin de yöntemi zaten güvenli değil. Eksik im,
kararı ancak **başka türlü saklanabilir olan** bir yanıtta ödetir. Ölçümde bu koşulu
sağlayan alışveriş yok, o yüzden bu satır temiz kalıyor. Aynı eksiklik güvenli yöntemli bir
alışverişte olsaydı yanlış sayısı ikiden dörde çıkardı.

İki yanlış, iki yüz kararlık bir kümede **0,0100**'lük bir paydır. Bu kümede ölçülebilen en
küçük fark **1/200 = 0,0050**'dir; iki yanlış onun iki katıdır ve ölçüm bandının içindedir.
Bundan küçük bir fark bu kümeyle savunulamaz.

Son tabloda açık rejim **195** doğru, **5** yanlış, **0** alınamayan veriyor. Beş yanlışın
**2**'si buradaki eksik imden geliyor; kalan üçü `taze` satırında duruyor ve bambaşka bir
alandan, doğrulayıcı damgasından kaynaklanıyor. O alan bu derste ölçülmüyor.

#### Yanlış Kurulmuş Kapsam ve Daraltmalar

Kapsamın geniş kurulması bir saldırı değil, bir **yapılandırma kusurudur**: kural yazılmış,
sözdizimi doğru, çerez çalışıyor ve hiçbir belirti vermiyor. Sayılan üç yüzey ve her birini
kapatan alan şunlardır.

**Birinci yüzey — gereksiz taşıma.** Konağın tümüne açılmış kapsamda çerez **23** iletide
hiçbir işe yaramadan taşınıyor. Bu iletilerin her biri aracının önünden geçiyor ve aracı
çerez alanını okuyabiliyor. **Daraltma:** `Path` niteliği oturum yoluna çekilirse gereksiz
taşıma **0**'a iner; bedeli, kapsam dışında kalan **6** kişiye özel yanıttır. `Domain`
niteliğinin verilmemesi kapsamı tek konağa bağlar; verilmesi onu alt adlara açar. `SameSite`
niteliği kapsamı yola göre değil, **isteği başlatan bağlama** göre daraltır ve `Path`
daraltmasının kapatamadığı bir yönü kapatır: aynı yola giden ama başka bir kaynaktan doğan
istek.

**İkinci yüzey — imsiz saklanan yanıt.** İki alışverişte aracı yanlış `paylasilabilir`
kararı veriyor. Böyle bir yanıtın paylaşılan bir önbelleğe konması, kişiye özel bir kopyanın
başka bir isteğe verilmesi demektir. **Daraltma:** yanıta `Cache-Control: private` konması
kararı doğrulur; `Vary: Cookie` ise saklanan kopyayı `Cookie` alanına bağlar ve aynı adresin
kopyalarını birbirinden ayırır. İkisi de sunucunun bildirimidir ve konmadıklarında aracı
bunu anlayamaz.

**Üçüncü yüzey — aracının imi görmesi.** Aracı hangi iletinin kişiye özel olduğunu okuyor;
bu, kararları gidiş–dönüşsüz almasının koşulu. **Daraltma:** im alanı taşıma zarfına
alınırsa aracı onu göremez. Son tablodaki `im zarfta` satırı bedeli veriyor: **80** karar
ortadan uçlara taşınıyor, kırk alışverişin kırkında birer tur daha atılıyor ve bayt
**3524**'ten **2859**'a iniyor. Kazanç ise yanlış sayısının **5**'ten **3**'e inmesidir —
aracının alamadığı karar, yanlış alamadığı karardır.

#### Özet

- Çerez, durumu istemcide tutarak durumsuz protokolde durum taşır; nitelikler yanıtta
  `Set-Cookie` ile gider, geri dönüşte yalnız ad–değer çifti `Cookie` alanında taşınır.
- Kapsam kuralı çerezin **hangi iletide** taşınacağını, im kuralı saklanan yanıtın **kime**
  gideceğini belirler; ikisi bağımsızdır ve ayrı ayrı yanlış kurulabilir.
- Üç kapsam kuralı sırasıyla **11**, **24** ve **40** ileti taşır; kapsam genişledikçe
  dışarıda kalan kişiye özel yanıt **6**'dan **0**'a iner, gereksiz taşıma **0**'dan
  **23**'e çıkar.
- Kırk alışverişin **17**'si kişiye özel, **2**'sinde im konmamış; aracı iki yanlış
  `paylasilabilir` kararı verir. `saklanabilir` temiz kalır çünkü o iki alışverişin yöntemi
  zaten güvenli değildir.
- İm alanı taşıma zarfına alındığında yanlış **5**'ten **3**'e iner ama **80** karar uçlara
  taşınır ve kırk tur borcu doğar; gizlemenin bedeli budur.

#### Sonraki Adım

Çerez bir başlık alanıdır ve kapsamı içindeki **her** istekte yeniden gönderilir. Kırk
alışverişte konak adı kırk kez, yöntem kırk kez, çerez kapsamı ne kadarsa o kadar kez
tekrarlanır — hepsi aynı bağlantı üzerinde, hepsi baştan yazılarak. Sonraki ders bu
tekrarın nasıl paketlendiğine bakar: metin satırından ikili çerçeveye geçiş aracıya ne
kazandırır, başlık sıkıştırması hangi alanı onun için okunmaz hâle getirir ve okunmayan
alanın kararı nereye taşınır.

### HTTP Sürümleri

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/http-surumleri

Aynı alanlar üç çerçeveleme rejiminde ölçülür: metin satırı 3524 bayt ve 251 bekleme birimi, bağlamı tutulan ikili çerçeve aynı 200 kararı 427 baytla verir, bağlamı tutulmayan ikili çerçevede 189 karar ortadan uçlara taşınır.

Çerez bir başlık alanıdır ve kapsamı içindeki her istekte yeniden gönderilir. Konak adı da
öyle, yöntem de, istemcinin bildirdiği tercihler de. Kırk alışverişlik bir oturumda aynı
dizgiler kırk kez tel üzerinden geçer ve her seferinde baştan yazılır.

Bu tekrar bir tasarım kararının sonucudur: HTTP iletisi **metin satırlarından** kurulur ve
metin satırı kendi kendine yeter. Ayrıştırıcı bir satır okur, iki noktayı bulur, adı ve
değeri ayırır. Sürümler arasındaki fark bu dersin konusudur ve fark **anlamda değil,
çerçevelemededir (framing)**: hangi alanların taşındığı değişmez, alanların tel üzerinde
nasıl paketlendiği değişir. Sorumuz şudur — paketlemenin değişmesi aradaki aracıya ne
kazandırır ve ne kaybettirir?

#### Aynı Anlam, Başka Çerçeveleme

HTTP/1.1 iletiyi satır satır yazar. İstek satırı yöntemi ve yolu taşır, ardından her satır
bir başlık alanıdır, boş satır gövdenin başladığını bildirir. HTTP/2 ve HTTP/3 aynı alanları
**ikili çerçevelere** koyar: her çerçevenin bir türü ve bir uzunluğu vardır, başlık alanları
ayrı bir kodlamayla sıkıştırılır. HTTP/2 çerçevesi ayrıca bir akış numarası ve bayraklar
taşır; HTTP/3'te bu iki alan yoktur, çünkü akışı taşımanın kendisi ayırır.

```text
# ogretilen dokum , calistirilmamistir

# metin satiri cercevelemesi
GET /olcum/kuzey HTTP/1.1
Host: istasyon.ornek
Cookie: oturum=k7
Accept: text/csv
Connection: keep-alive

# ikili cerceveleme , ayni alanlar
CERCEVE tur=HEADERS akis=7 bayrak=END_HEADERS
  :method     -> dizin
  :path       -> sozluk  /olcum/kuzey
  :authority  -> dizin
  cookie      -> dizin
  accept      -> dizin
CERCEVE tur=DATA akis=7 bayrak=END_STREAM
```

İki biçim aynı şeyi söyler. Yöntem, yol, konak ve iki tercih alanı ikisinde de vardır;
`:method` ve `:path` adları metin biçimindeki istek satırının karşılığıdır. Değişen üç şey
şudur: alanlar **satır değil çerçeve** taşır, çerçevelerin bir **akış numarası** vardır ve
başlık alanları **dizinlenerek** gönderilebilir.

Akış numarası çoğullamayı (multiplexing) mümkün kılar: tek bağlantı üzerinde birden çok
alışveriş iç içe ilerleyebilir, çünkü her çerçeve hangi akışa ait olduğunu kendi taşır.
Metin satırı çerçevelemesinde böyle bir alan yoktur; bir bağlantıda istekler ancak ardışık
gidebilir ve bu **kalıcı bağlantıda** da böyledir — bağlantı açık kalır ama sıra korunur.

HTTP/3 çerçevelemeyi değil, **taşımayı** değiştirir. Çerçeve düzeni benzer kalır; altındaki
taşıma katmanı tek sıralı bayt akışı yerine birbirinden bağımsız akışlar sunar. Bu ayrımın
neden önemli olduğu bir sonraki bölümün konusudur.

#### Sıra Başı Engellemesi İki Katmanda

**Sıra başı engellemesi (head-of-line blocking)**, bir kuyruğun başındaki öğe ilerleyemediği
için arkasındakilerin de beklemesidir. HTTP'de bunun iki ayrı biçimi vardır ve
karıştırılmaları sürümler arası farkı görünmez kılar.

**Uygulama katmanındaki biçim:** tek bağlantıda istekler ardışıksa, yavaş bir yanıt
arkasındaki bütün alışverişleri bekletir. Yavaşlık ağdan gelmez — sunucu o yanıtı üretmek
için birden çok kaydı derliyordur. Çoğullama bu biçimi kaldırır.

**Taşıma katmanındaki biçim:** taşıma katmanı sıralı teslim veriyorsa, kaybolan bir parça
onun ardından gelen her şeyi bekletir; hangi akışa ait olduklarına bakmaz, çünkü taşıma
katmanı akışları bilmez. Çoğullama bu biçimi **kaldırmaz**, yalnız yukarı katmandan aşağıya
taşır. Kaldıran şey taşımanın kendisinin akış farkındalığı kazanmasıdır.

Bu ayrım, **Ağ Modelleri ve Protokoller** kursunda kurulan bayt akışı soyutlamasının
doğrudan sonucudur: sıralı ve kayıpsız bir akış sözü verildiğinde, o sözü tutmak için
bekleyen bir kuyruk gerekir. **İnternet Nasıl Çalışır** kursundaki giriş düzeyi sezgi
burada bir sayıya bağlanır.

Engellemenin aracı için ayrı bir anlamı vardır. Aracı iletiyi yalnız okumaz, **iletir**; o
da bir kuyruktur. Uygulama sırası korunuyorsa aracı elindeki yanıtı sırası gelmeden
gönderemez ve bir yanıtı beklerken ardındakileri tutar. Çoğullamanın kaldırdığı şey
sunucunun değil, aracının kuyruğudur.

#### Başlık Sıkıştırması ve Aracının Bağlamı

İkili çerçeveleme başlık alanlarını dizinleyerek gönderir: bağlantı boyunca bir ad–değer
tablosu tutulur, bir çift ikinci kez geçtiğinde adı ve değeri değil **tablodaki sırası**
gönderilir. Kazanç büyüktür, çünkü konak adı ve yöntem gibi alanlar hemen hemen hiç
değişmez.

Bedeli şudur: **dizin, tabloyu tutmayan için anlamsızdır.** Aracı bağlantının başından beri
oradaysa tabloyu kendi de kurar ve her alanı çözer. Bağlantıya sonradan katılmışsa, ya da
çerçeveleri çözmeden ileten bir katmansa, yalnız sözlük olarak yazılmış alanları okuyabilir.

- **HA46** — Aynı kırk alışveriş, aynı beş alan ve aynı kâhin kullanılır; değişen tek şey
  alanların tel üzerinde nasıl yazıldığıdır.
- **HA47** — Metin satırı çerçevelemesinde her alan her iletide baştan yazılır: ad, değer ve
  ayraç.
- **HA48** — İkili çerçevelemede bir ad–değer çifti ilk geçtiğinde sözlük olarak yazılır ve
  tabloya girer; sonraki geçişlerinde **1 baytlık** dizin gönderilir.
- **HA49** — Tabloyu tutan aracı bütün alanları çözer. Tutmayan aracı yalnız sözlük olarak
  yazılmış alanı okur; dizinli alan onun için yoktur.
- **HA50** — Uygulama tarafında yavaş olan alışveriş `/ozet` yoludur; sunucu o yanıtı birden
  çok kayıttan derler.
- **HA51** — Taşımada kayıp yaşayan alışveriş ayrı bir üreteçle, tek modülüsle seçilir.
- **HA52** — Bekleme birimi bir süre değil bir sayımdır: bir alışveriş, aynı kuyrukta
  kendinden önce duran her engelin arkasında bir birim bekler.
- **HA53** — Paralel bağlantıda alışverişler sıra ile bağlantılara dağıtılır ve **her
  bağlantının kendi tablosu vardır.**
- **HA54** — Ölçüm bir ağ ölçümü değildir; bayt sayımı ileti içeriğinin sayımıdır.

#### Ölçüm

```python
"""HTTP surumleri: cercevelemenin araciya kazandirdigi ve kaybettirdigi.

Bolum 1 - ayni alanlar uc cerceveleme rejiminde: karar ve bayt.
Bolum 2 - sira basi engellemesi ve cogullama: bekleyen alisveris.
"""
TOHUM = 20260809
GUVENLI = {"GET", "HEAD"}
ETKISIZ = {"GET", "HEAD", "PUT", "DELETE"}
YONTEMLER = ["GET", "GET", "GET", "HEAD", "POST", "PUT", "DELETE"]
YOLLAR = ["/olcum/kuzey", "/olcum/yamac", "/ozet", "/kayit", "/oturum"]
KARARLAR = ("saklanabilir", "paylasilabilir", "taze", "yinelenebilir",
            "yonlendirilebilir")
ALANLAR = ("yontem", "yol", "konak", "ozel_imi", "dogrulayici")


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def alisverisler(sayi=40):
    r, liste = uretec(TOHUM), []
    for i in range(sayi):
        yontem, yol = YONTEMLER[r(7)], YOLLAR[r(5)]
        ozel = yol == "/oturum" or r(5) == 0
        degisti = r(3) == 0
        liste.append({"no": i + 1, "yontem": yontem, "yol": yol,
                      "ozel": ozel, "degisti": degisti,
                      "ozel_imi": ozel and r(4) != 0,
                      "damga_atladi": degisti and r(6) == 0})
    return liste


def kahin(a):
    return {"saklanabilir": a["yontem"] in GUVENLI and not a["ozel"],
            "paylasilabilir": not a["ozel"], "taze": not a["degisti"],
            "yinelenebilir": a["yontem"] in ETKISIZ,
            "yonlendirilebilir": True}


def ileti(a):
    return {"yontem": a["yontem"], "yol": a["yol"], "konak": "istasyon.ornek",
            "ozel_imi": a["ozel_imi"],
            "dogrulayici": a["degisti"] and not a["damga_atladi"]}


def araci(i):
    k = {}
    if "yontem" in i:
        k["yinelenebilir"] = i["yontem"] in ETKISIZ
    if "ozel_imi" in i:
        k["paylasilabilir"] = not i["ozel_imi"]
        if "yontem" in i:
            k["saklanabilir"] = i["yontem"] in GUVENLI and not i["ozel_imi"]
    if "dogrulayici" in i:
        k["taze"] = not i["dogrulayici"]
    if "konak" in i:
        k["yonlendirilebilir"] = True
    return k


def cercevele(A, baglanti=1, sikistir=True):
    """Her iletiyi (bayt, sozluk-olarak-yazilan alanlar) ciftine cevirir.
    Ayni baglantida daha once gecen ad-deger cifti dizin olarak gonderilir:
    1 bayt tutar ve tabloyu tutmayan araci onu cozemez."""
    tablo, cikti = [set() for _ in range(baglanti)], []
    for i, a in enumerate(A):
        t, b, sozluk = tablo[i % baglanti], 0, set()
        for k, v in ileti(a).items():
            if not sikistir or (k, v) not in t:
                b += len(k) + len(str(v)) + 4
                sozluk.add(k)
                t.add((k, v))
            else:
                b += 1
        cikti.append((b, sozluk))
    return cikti


def olc(A, cerceve, baglam):
    d = y = e = t = b = 0
    for a, (bayt, sozluk) in zip(A, cerceve):
        gorunur = set(ALANLAR) if baglam else sozluk
        gercek = kahin(a)
        verilen = araci({k: v for k, v in ileti(a).items() if k in gorunur})
        eksik = False
        for karar in KARARLAR:
            if karar not in verilen:
                e += 1
                eksik = True
            elif verilen[karar] == gercek[karar]:
                d += 1
            else:
                y += 1
        t += 1 if eksik else 0
        b += bayt
    return d, y, e, t, b


def bekleme(A, engel, baglanti=1):
    """Bir alisveris, ayni kuyrukta kendinden once duran her engelin
    arkasinda bir birim bekler."""
    onceki, toplam = [0] * baglanti, 0
    for i, e in enumerate(engel):
        toplam += onceki[i % baglanti]
        onceki[i % baglanti] += 1 if e else 0
    return toplam


A = alisverisler()
kayip = uretec(TOHUM + 1)
YAVAS = [a["yol"] == "/ozet" for a in A]
KAYIP = [kayip(5) == 0 for _ in A]
IKISI = [u or k for u, k in zip(YAVAS, KAYIP)]
print(f"alışveriş {len(A)} | karar {len(A) * len(KARARLAR)} | "
      f"derlenen yanıt {sum(YAVAS)} | taşımada kayıp yaşayan {sum(KAYIP)}")
print()
print("çerçeveleme rejimi        doğru  yanlış  alınamayan  fazladan tur   bayt")
for ad, cerceve, baglam in (
        ("metin satırı", cercevele(A, 1, False), True),
        ("ikili, bağlam tutulur", cercevele(A), True),
        ("ikili, bağlam yok", cercevele(A), False)):
    d, y, e, t, b = olc(A, cerceve, baglam)
    print(f"  {ad:23s} {d:5d} {y:7d} {e:11d} {t:13d} {b:6d}")
print()
print("çerçeveleme rejimi        uygulama sırası  taşıma sırası  bekleyen alışveriş")
for ad, u, ta in (("metin satırı, tek bağlantı", True, True),
                  ("ikili, tek taşıma akışı", False, True),
                  ("ikili, ayrı taşıma akışları", False, False)):
    engel = IKISI if u and ta else KAYIP if ta else [False] * len(A)
    print(f"  {ad:27s} {'var' if u else 'yok':>10s}"
          f" {'var' if ta else 'yok':>14s} {bekleme(A, engel):18d}")
print()
print("paralel bağlantı  bekleyen alışveriş  sözlük olarak yazılan alan   bayt")
for n in (1, 2, 6):
    c = cercevele(A, n)
    print(f"  {n:14d} {bekleme(A, IKISI, n):19d}"
          f" {sum(len(s) for _, s in c):27d} {sum(b for b, _ in c):6d}")
```

```
alışveriş 40 | karar 200 | derlenen yanıt 9 | taşımada kayıp yaşayan 7

çerçeveleme rejimi        doğru  yanlış  alınamayan  fazladan tur   bayt
  metin satırı              195       5           0             0   3524
  ikili, bağlam tutulur     195       5           0             0    427
  ikili, bağlam yok          10       1         189            39    427

çerçeveleme rejimi        uygulama sırası  taşıma sırası  bekleyen alışveriş
  metin satırı, tek bağlantı         var            var                251
  ikili, tek taşıma akışı            yok            var                 83
  ikili, ayrı taşıma akışları        yok            yok                  0

paralel bağlantı  bekleyen alışveriş  sözlük olarak yazılan alan   bayt
               1                 251                          15    427
               2                 121                          29    636
               6                  36                          74   1321
```

#### Kazanç: Aynı Karar, Sekizde Bir Bayt

İlk tablonun ilk iki satırı yan yana okunmalıdır. Metin satırı çerçevelemesi **195** doğru,
**5** yanlış, **0** alınamayan veriyor ve **3524** bayt harcıyor. Bağlamı tutulan ikili
çerçeveleme **aynı** üç sayıyı veriyor ve **427** bayt harcıyor. Karar tablosu değişmiyor;
bayt **%87,9** azalıyor.

Bu, ortak kurgunun ikinci iddiasının tersten okunuşudur. Alan eklemek karar kazandırmıyordu;
şimdi görülüyor ki **alanı ucuzlatmak da karar kaybettirmiyor.** Aracının çözebildiği sürece
alanın kaç bayt tuttuğu kararla ilgisizdir. Kırk alışverişte iki yüz alan geçiyor ve
bunların yalnız **15**'i ayrı bir ad–değer çifti; kalan 185 geçiş aynı on beş çiftin
tekrarı. Sıkıştırmanın kazandığı yer tam burasıdır.

İkinci tablo ikinci kazancı veriyor. Metin satırı çerçevelemesinde tek bağlantıda toplam
**251** bekleme birimi doğuyor: dokuz derlenen yanıt ve yedi kayıp, arkalarındaki her
alışverişi bekletiyor. Çoğullama uygulama sırasını kaldırınca bekleme **83**'e iniyor —
kalan tamamı taşıma katmanından geliyor. Taşıma da akışları ayırdığında **0**.

Üç satırın sırası önemlidir: çoğullama beklemeyi sıfırlamaz, **üçte birine indirir**;
sıfırlayan şey taşımanın değişmesidir. Çerçeveleme katmanında çözülen sorunun kalanı
aşağıdaki katmandaydı.

#### Kayıp: Çözülemeyen Alan Yok Sayılır

Üçüncü satır bu dersin asıl bulgusudur. Bayt aynı **427**, ama karar tablosu çöküyor:
**10** doğru, **1** yanlış, **189** alınamayan, **39** fazladan tur.

Nedeni şudur: kırk iletide yalnız **15** alan sözlük olarak yazılmış; kalan 185 alan
dizinle gönderilmiş. Tabloyu tutmayan aracı o 185 alanı okuyamıyor ve okuyamadığı alandan
karar çıkaramıyor. İki yüz kararın **189**'u ortadan uçlara taşınıyor — zarf rejiminin
taşıdığı 160'tan da fazla. Alanlar iletide **duruyor**; aracı için var olmuyorlar.

Kalan on bir karar ilk alışverişten ve tablonun kurulduğu birkaç ilk geçişten geliyor. Tek
yanlış karar da orada: birinci alışveriş `POST /kayit` ve kişiye özel bir yanıt üretiyor ama
sunucu imi koymamış. Aracı o iletide alanı okuyabildiği için karar veriyor ve **yanlış**
veriyor. Örüntü değişmiyor — okunabilen alan yanlış olabilir, okunamayan alan yalnız
eksiktir.

Buradan çıkan kural şudur: **sıkıştırma bir görünürlük kararıdır.** Bağlantının başında
duran aracı hiçbir şey kaybetmez; sonradan katılan ya da çerçeveyi çözmeden ileten aracı
neredeyse her şeyi kaybeder. Metin satırı çerçevelemesinde böyle bir ayrım yoktu, çünkü her
ileti kendi kendine yeterdi.

#### Paralel Bağlantı: Aynı Sorunun Başka Bedeli

Üçüncü tablo, çoğullamadan önceki çözümü ölçüyor: aynı konağa birden çok bağlantı açmak.
Bekleme gerçekten düşüyor — **251**'den iki bağlantıda **121**'e, altı bağlantıda **36**'ya.

Bedeli sağdaki iki sütunda. Her bağlantının kendi başlık tablosu vardır; alışverişler
bölündükçe aynı ad–değer çiftleri her bağlantıda yeniden sözlük olarak yazılır. Sözlük
olarak yazılan alan sayısı **15**'ten **29**'a, sonra **74**'e çıkıyor; bayt **427**'den
**636**'ya, sonra **1321**'e. Altı bağlantıda beklemenin yedide birine inmesi, baytın üç
katına çıkmasıyla ödeniyor.

Çoğullamanın farkı buradan çıkıyor: tek bağlantıda **hem** beklemeyi taşıma katmanına
indiriyor **hem** tabloyu tek parça tutuyor. Paralel bağlantı ise iki maliyeti takas ediyor,
birini ortadan kaldırmıyor.

#### Özet

- Sürümler arasındaki fark anlamda değil çerçevelemededir: aynı alanlar metin satırı ya da
  akış numaralı ikili çerçeve olarak yazılır.
- Bağlamı tutulan ikili çerçeveleme **aynı** 195 doğru ve 5 yanlış kararı **3524** yerine
  **427** baytla verir; kırk alışverişteki 200 alan geçişinin yalnız **15**'i ayrı bir
  ad–değer çiftidir.
- Sıra başı engellemesinin iki biçimi vardır; çoğullama uygulama katmanındakini kaldırır
  ve beklemeyi **251**'den **83**'e indirir, taşıma katmanındakini ancak taşımanın akış
  farkındalığı **0**'a indirir.
- Bağlamı tutmayan aracı yalnız sözlük olarak yazılmış alanı okur: **189** karar uçlara
  taşınır, **39** tur borcu doğar ve bayt hiç değişmez.
- Paralel bağlantı beklemeyi **36**'ya indirir ama tabloyu böler; sözlük olarak yazılan alan
  **74**'e, bayt **1321**'e çıkar.

#### Sonraki Adım

Çerçeveleme aracının alanı okuyup okumadığını belirliyor. Ama okuyabildiği hâlde bile bir
karar var ki alanın kendisinden değil, **iki değerin karşılaştırılmasından** geliyor: elde
tutulan kopya ile yeni gelen ileti aynı şeyi mi anlatıyor? Sonraki ders bu karşılaştırmayı
ölçer. Aracının sakladığı kopyanın hâlâ geçerli olup olmadığını hangi alan söyler, o alanın
çözünürlüğü nedir ve çözünürlük yetmediğinde aracı ne der.

### HTTP Önbellekleme

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/http-onbellekleme

Tazelik ile doğrulama ayrı sorulardır ve doğrulayıcının bir çözünürlüğü vardır: kırk alışverişteki 12 bayat kopyanın 9'unu yakalar, 3'ünde damga değişimini kaçırır ve aracı taze der — açık rejimdeki beş yanlış kararın üçü buradan gelir.

Önceki ders çerçevelemenin aracının hangi alanı okuyabildiğini belirlediğini ölçtü. Ama
alanı okuyabilmek her zaman yetmez. Aracının verdiği beş karardan dördü tek bir alana bakar:
yöntem yinelenebilirliği, im paylaşılabilirliği söyler. Beşincisi başkadır. **Tazelik**, tek
bir alanın değerinden çıkmaz; elde tutulan kopya ile yeni gelen iletinin **karşılaştırılmasından**
çıkar.

Karşılaştırma yeni bir sorun getirir. İki değeri kıyaslayan her düzeneğin bir **çözünürlüğü**
vardır: ayırt edebildiğinden küçük bir fark, ona göre fark değildir. Bu dersin ölçtüğü şey
budur — aracının sakladığı kopyanın bayat olduğunu ne kadar sık fark ettiği ve fark
edemediğinde ne dediği.

#### Tazelik ve Doğrulama: İki Ayrı Soru

Aracının önbelleğindeki bir kopya için iki ayrı soru vardır.

**Tazelik sorusu:** bu kopyayı **sormadan** verebilir miyim? Yanıtı sunucunun bildirdiği
**tazelik ömrüdür (freshness lifetime)**. Ömür içindeyse aracı kopyayı doğrudan verir;
sunucuya hiç dönmez, hiç tur harcamaz.

**Doğrulama sorusu:** ömür dolduysa kopya hâlâ geçerli mi? Yanıtı **doğrulayıcı
(validator)** verir: sunucunun gösterime iliştirdiği bir damga. Aracı elindeki damgayla
sunucudakini karşılaştırır; aynıysa kopya geçerlidir.

İkisi karıştırılırsa ölçü kaybolur. Tazelik bir **süre** kararıdır ve tur harcamaz.
Doğrulama bir **kimlik** kararıdır ve gerçeği verir. Önbelleğin bütün tasarımı bu ikisi
arasındaki takasa dayanır: süre uzarsa tur azalır ve bayat kopya verme olasılığı büyür.

Bu iki sorudan önce bir üçüncüsü gelir ve önceki iki ders onu kurdu: bu yanıt
**saklanabilir** mi, saklandıysa **paylaşılabilir** mi? Aracı bir kopyayı ancak yöntem
güvenliyse saklar ve ancak kişiye özel im yoksa başkasına verir. Tazelik sorusu bu iki
kararın ardından gelir; saklanmayan bir kopyanın tazeliği diye bir soru yoktur. Ölçümde bu
sıra korunur: üç rejim de aynı `saklanabilir` ve `paylasilabilir` kararlarını verir,
aralarındaki tek fark tazeliktir.

Bu kursta ölçülen şey direktif listesi ya da önbelleğin kazandırdığı gövde değildir; bunlar
**Önbellekleme, Kuyruklar ve Eşzamansız İşleme** kursunda kuruldu. Burada ölçülen tek şey
**doğrulayıcının çözünürlüğüdür** — damganın hangi değişimleri gördüğü ve hangilerini
görmediği.

#### Doğrulayıcının Çözünürlüğü

HTTP iki tür doğrulayıcı tanımlar ve ikisinin çözünürlüğü aynı değildir.

**Zaman damgası** doğrulayıcısı gösterimin son değişme anını taşır. Çözünürlüğü, damganın
yazıldığı zaman birimidir: aynı saniye içinde iki kez değişen bir gösterim, iki değişimden
sonra da aynı damgayı taşır. Aracı damgayı aynı görür ve kopyayı geçerli sayar.

**Varlık etiketi** doğrulayıcısı gösterimin içeriğinden türetilen bir dizgidir ve zaman
birimine bağlı değildir. Etiketin **güçlü** ve **zayıf** iki biçimi vardır. Güçlü etiket bayt
düzeyinde ayrımı taşır. Zayıf etiket, anlamca önemsiz sayılan değişimleri kasten göz ardı
eder — yani **çözünürlüğü bilerek düşürülmüş** bir doğrulayıcıdır.

```text
# ogretilen dokum , calistirilmamistir

# sunucunun yanitta tasidigi dogrulayicilar
HTTP/1.1 200 OK
Last-Modified: Sun, 09 Aug 2026 11:04:07 GMT
ETag: "olcum-7a"
Cache-Control: max-age=60
Age: 12

# aracinin elindeki kopyayi sinamak icin kurdugu kosullu istek
GET /olcum/kuzey HTTP/1.1
Host: istasyon.ornek
If-None-Match: "olcum-7a"
If-Modified-Since: Sun, 09 Aug 2026 11:04:07 GMT

# damga ayni ise
HTTP/1.1 304 Not Modified
ETag: "olcum-7a"
```

`Cache-Control` tazelik ömrünü, `Age` kopyanın önbellekte geçirdiği süreyi bildirir; ikisi
tazelik sorusunu yanıtlar. `If-None-Match` ve `If-Modified-Since` **koşullu isteği** kurar,
`304` ise damganın aynı kaldığını bildiren durum kodudur. Ölçümde bu alanların hepsi tek bir
bildirime indirgenir: iletide taşınan damga, aracının elindekinden farklı mı?

`Age` alanının ayrı bir işlevi vardır. Aracı sakladığı kopyanın yaşını bu alanla bildirir ki
zincirdeki bir sonraki aracı aynı hesabı yapabilsin. Birden çok aracı varsa yaş toplanarak
taşınır ve tazelik ömrü zincirin tamamı için **tek bir bütçedir**; her durak kendi payını
harcar.

Damganın kendisi de iletide yazandan başka bir şey değildir. **Doğrulayıcıyı doğrulayan bir
şey yoktur:** damga değişmediyse aracı gösterimin değişmediğine inanır, çünkü elinde başka
bir dayanak yoktur.

- **HA55** — Aynı kırk alışveriş ve aynı kâhin kullanılır; kâhin, aracının elindeki kopyanın
  gerçekten bayat olup olmadığını bilir.
- **HA56** — Aracı yolun üzerindedir ve geçen yanıtları görür; elindeki damgayla iletide
  gelen damgayı karşılaştırır. Karşılaştırma **fazladan bir gidiş–dönüş istemez**, çünkü
  ileti zaten aracının önünden geçmektedir.
- **HA57** — Damganın bir çözünürlüğü vardır: değişim damganın ayırt edebileceğinden küçükse
  damga aynı kalır ve aracı değişimi göremez.
- **HA58** — Üç rejim ölçülür. `yalnız süre` rejiminde iletide tazelik ömrü bildirilir,
  doğrulayıcı bildirilmez. `damga` rejiminde doğrulayıcı bildirilir. `her seferinde sor`
  rejiminde ikisi de bildirilmez.
- **HA59** — Ölçüm penceresinde tazelik ömrü dolmaz; `yalnız süre` rejiminde aracı her
  kopyayı taze sayar.
- **HA60** — Tazelik kararını alamayan aracı koşullu bir istek gönderir. Sunucu gerçeği
  bilir, bu yüzden **tur sonrası** yakalama tamdır.
- **HA61** — Bayt sayımı ileti alanlarının sayımıdır; gövde sayılmaz.

#### Ölçüm

```python
"""HTTP onbellekleme: tazelik , dogrulama ve dogrulayicinin cozunurlugu.

Bolum 1 - dogrulayici bayat kopyalarin kacini yakaliyor.
Bolum 2 - uc rejim: yalniz sure , damga , her seferinde sor.
"""
TOHUM = 20260809
GUVENLI = {"GET", "HEAD"}
ETKISIZ = {"GET", "HEAD", "PUT", "DELETE"}
YONTEMLER = ["GET", "GET", "GET", "HEAD", "POST", "PUT", "DELETE"]
YOLLAR = ["/olcum/kuzey", "/olcum/yamac", "/ozet", "/kayit", "/oturum"]
KARARLAR = ("saklanabilir", "paylasilabilir", "taze", "yinelenebilir",
            "yonlendirilebilir")


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def alisverisler(sayi=40):
    r, liste = uretec(TOHUM), []
    for i in range(sayi):
        yontem, yol = YONTEMLER[r(7)], YOLLAR[r(5)]
        ozel = yol == "/oturum" or r(5) == 0
        degisti = r(3) == 0
        liste.append({"no": i + 1, "yontem": yontem, "yol": yol,
                      "ozel": ozel, "degisti": degisti,
                      "ozel_imi": ozel and r(4) != 0,
                      "damga_atladi": degisti and r(6) == 0})
    return liste


def kahin(a):
    return {"saklanabilir": a["yontem"] in GUVENLI and not a["ozel"],
            "paylasilabilir": not a["ozel"], "taze": not a["degisti"],
            "yinelenebilir": a["yontem"] in ETKISIZ,
            "yonlendirilebilir": True}


def ileti(a, gorunur):
    tam = {"yontem": a["yontem"], "yol": a["yol"], "konak": "istasyon.ornek",
           "ozel_imi": a["ozel_imi"],
           "dogrulayici": a["degisti"] and not a["damga_atladi"],
           "tazelik_omru": True}
    return {k: v for k, v in tam.items() if k in gorunur}


def bayt(i):
    return sum(len(k) + len(str(v)) + 4 for k, v in i.items())


def araci(i):
    k = {}
    if "yontem" in i:
        k["yinelenebilir"] = i["yontem"] in ETKISIZ
    if "ozel_imi" in i:
        k["paylasilabilir"] = not i["ozel_imi"]
        if "yontem" in i:
            k["saklanabilir"] = i["yontem"] in GUVENLI and not i["ozel_imi"]
    if "dogrulayici" in i:
        k["taze"] = not i["dogrulayici"]
    elif "tazelik_omru" in i:
        k["taze"] = i["tazelik_omru"]
    if "konak" in i:
        k["yonlendirilebilir"] = True
    return k


ORTAK = ("yontem", "yol", "konak", "ozel_imi")
GORUNUR = {"yalnız süre": ORTAK + ("tazelik_omru",),
           "damga": ORTAK + ("dogrulayici",),
           "her seferinde sor": ORTAK}
A = alisverisler()
BAYAT = [a for a in A if a["degisti"]]
KACAN = [a for a in BAYAT if a["damga_atladi"]]
print(f"alışveriş {len(A)} | karar {len(A) * len(KARARLAR)} | "
      f"bayat kopya {len(BAYAT)} | damga değişimi kaçan {len(KACAN)}")
print("kaçan alışverişler:", [(a["no"], a["yol"]) for a in KACAN])
print()
print(f"  {'rejim':18s}{'yakalanan':>10s}{'kaçan':>7s}{'çözünürlük':>12s}"
      f"{'sorulan tur':>13s}{'tur sonrası':>13s}")
for ad in GORUNUR:
    yakalanan = sum(1 for a in BAYAT
                    if araci(ileti(a, GORUNUR[ad])).get("taze") is False)
    sorulan = sum(1 for a in A if "taze" not in araci(ileti(a, GORUNUR[ad])))
    print(f"  {ad:18s}{yakalanan:>10d}{len(BAYAT) - yakalanan:>7d}"
          f"{yakalanan / len(BAYAT):>12.4f}{sorulan:>13d}"
          f"{len(BAYAT) if sorulan else yakalanan:>13d}")
print()
print(f"  {'rejim':18s}{'karar':9s}{'doğru':>7s}{'yanlış':>8s}"
      f"{'alınamayan':>12s}{'fazladan tur':>14s}{'bayt':>7s}")
for ad in GORUNUR:
    td = ty = te = d = y = e = t = b = 0
    for a in A:
        gercek, verilen = kahin(a), araci(ileti(a, GORUNUR[ad]))
        eksik = False
        for karar in KARARLAR:
            if karar not in verilen:
                e += 1
                eksik = True
                te += karar == "taze"
            elif verilen[karar] == gercek[karar]:
                d += 1
                td += karar == "taze"
            else:
                y += 1
                ty += karar == "taze"
        t += 1 if eksik else 0
        b += bayt(ileti(a, GORUNUR[ad]))
    print(f"  {ad:18s}{'tazelik':9s}{td:>7d}{ty:>8d}{te:>12d}")
    print(f"  {ad:18s}{'bütün':9s}{d:>7d}{y:>8d}{e:>12d}{t:>14d}{b:>7d}")
```

```
alışveriş 40 | karar 200 | bayat kopya 12 | damga değişimi kaçan 3
kaçan alışverişler: [(24, '/ozet'), (25, '/oturum'), (37, '/olcum/yamac')]

  rejim              yakalanan  kaçan  çözünürlük  sorulan tur  tur sonrası
  yalnız süre                0     12      0.0000            0            0
  damga                      9      3      0.7500            0            9
  her seferinde sor          0     12      0.0000           40           12

  rejim             karar      doğru  yanlış  alınamayan  fazladan tur   bayt
  yalnız süre       tazelik       28      12           0
  yalnız süre       bütün        186      14           0             0   3533
  damga             tazelik       37       3           0
  damga             bütün        195       5           0             0   3524
  her seferinde sor tazelik        0       0          40
  her seferinde sor bütün        158       2          40            40   2733
```

#### Çözünürlük: Dörtte Üç

Üst tablonun orta satırı bu dersin sayısıdır. Kırk alışverişte aracının elindeki kopya
**12** kez bayattır. Doğrulayıcı bunların **9**'unda damganın değiştiğini görür ve kopyayı
bayat sayar. **3**'ünde damga değişmemiştir; aracı iki damgayı aynı görür ve kopyaya
**taze** der. Çözünürlük **0,7500**.

Üç kaçan alışverişin yolları farklı: `/ozet`, `/oturum` ve `/olcum/yamac`. Kaçırma bir yola
ya da bir yönteme bağlı değil — değişimin damganın ayırt edebileceğinden küçük olmasına
bağlı. Bu, çözünürlüğün tanımı gereği böyledir ve düzeltilebilir bir hata değildir; ancak
damgayı değiştirerek daraltılabilir bir aralıktır.

Bu üç kaçırmanın hiçbir belirtisi yoktur. Aracı kurala uygun çalışmış, damgaları doğru
karşılaştırmış, kopyayı doğru gerekçeyle vermiştir. Kararın yanlış olduğunu yalnız kâhin
bilir ve kâhin burada kurguyu bizim üretmemizden doğar. Gerçek bir dağıtımda o kâhin yoktur:
**bayat kopya, taze kopyayla aynı görünür.**

Saklanan kopyayı düşüren bir yol daha vardır ve tur harcamaz. Aracı, sakladığı adrese
güvenli olmayan bir yöntemle giden bir istek gördüğünde o kopyanın artık geçerli
olmayacağını çıkarabilir: aynı yola `PUT` ya da `DELETE` geçtiğinde elindeki kopyayı düşürür.
Bu da iletiden alınan bir karardır ve damganın çözünürlüğüne bağlı değildir. Sınırı şudur:
aracı yalnız **kendi önünden geçen** isteği görür. Aynı gösterimi başka bir yoldan
değiştiren bir istek onun önünden geçmez ve kopya sessizce bayatlar — sonuç, damganın
kaçırdığı üç alışverişle aynıdır.

Üst tablonun ilk satırı, doğrulayıcının olmadığı durumu veriyor. Aracı yalnız tazelik
ömrüne bakıyor, ömür de dolmuyor: **12** bayat kopyanın **hiçbirini** yakalamıyor.
Çözünürlük **0,0000**. Süre bir çözünürlük ölçüsü bile değildir, çünkü değişimi hiç
sınamaz — yalnız bir bahis koyar.

Son satır tersini yapıyor. Aracı tazelik kararını hiç almıyor, her alışverişte soruyor:
**40** tur. Karşılığında yakalama tam, **12/12**. Ama bu on iki kopyanın yakalanması için
kırk soru soruldu; yirmi sekizi zaten taze olan kopyalar için harcandı.

#### Üç Rejimin Karar Bedeli

Alt tablo aynı üç rejimi bütün karar kümesi üzerinden veriyor.

**`yalnız süre`** rejiminde tazelik kararı **28** doğru, **12** yanlış. Bütün küme
**186**/**14**/**0**. Yanlış sayısı en yüksek burada: aracı hiç sormadan, hiç eksik karar
bırakmadan on dört kez yanılıyor. Bayt **3533**, üç rejimin en yükseği.

**`damga`** rejiminde tazelik **37** doğru, **3** yanlış; bütün küme **195**/**5**/**0** ve
**0** tur. Ortak kurgunun açık rejimi tam olarak budur ve **beş yanlış kararın üçü bu
satırdan** geliyor. Kalan ikisi, sunucunun koymayı unuttuğu kişiye özel imden gelir ve
tazelikle ilgisi yoktur.

**`her seferinde sor`** rejiminde tazelik kararı hiç alınmıyor: **40** alınamayan. Bütün küme
**158**/**2**/**40** ve **40** tur. Yanlış **5**'ten **2**'ye iniyor. Kalan iki yanlış yine
kişiye özel imden — sormak onu düzeltmiyor, çünkü sorulan soru tazelikle ilgili.

Üç satır kursun eksenini bir kez daha veriyor. **Alınamayan karar bir tur borcudur:** kırk
tur, kırk kararın karşılığıdır. **Alınan karar ise yanlış olabilir:** damga rejimi kırk
tazelik kararını sıfır turla alıyor ve üçünde yanılıyor. Süre rejimi de sıfır tur harcıyor
ama on ikisinde yanılıyor — aynı bedelle dört kat yanlış.

#### Sıralama Neye Göre Yapılır

Üç rejim arasında tek bir "en iyi" yoktur; sıralama neyin pahalı olduğuna göre değişir.

Tur pahalıysa `damga` rejimi seçilir: sıfır tur, üç yanlış. Üç yanlış iki yüz kararlık
kümede **0,0150**'lik bir paydır; bu kümede ölçülebilen en küçük fark **1/200 = 0,0050**
olduğuna göre üç yanlış bandın içindedir ve savunulabilir bir sayıdır. Bayat kopya vermek
kabul edilemezse `her seferinde sor` seçilir ve kırk tur ödenir. `yalnız süre` rejimi ise
yalnız değişim beklenmeyen gösterimler için savunulabilir; ölçümde bayat oranı **12/40**
olduğu için burada savunulamaz.

Damganın çözünürlüğünü yükseltmek dördüncü bir yoldur ve tabloya girmez, çünkü ölçüm
damgayı veri olarak alır. Yine de yön bellidir: zaman damgası yerine varlık etiketi, zayıf
etiket yerine güçlü etiket, çözünürlüğü büyüten değil daraltan seçimdir. Her daraltma
kaçan üçü azaltır ve hiçbiri tur eklemez.

Son bir gözlem: `her seferinde sor` rejiminin baytı **2733**, `damga` rejiminin **3524**.
Sormak iletiyi küçültür, çünkü taşınmayan alan bayt tutmaz. Ama küçülen ileti, borçlanılan
kırk turun yanında hiçbir şeydir. **Bayt ucuzdur, tur pahalıdır** — kursun ölçü ekseni tam
olarak bunu söylüyordu.

#### Özet

- Tazelik ve doğrulama ayrı sorulardır: tazelik kopyanın **sormadan** verilip
  verilemeyeceğini, doğrulama kopyanın hâlâ geçerli olup olmadığını yanıtlar.
- Doğrulayıcının bir çözünürlüğü vardır; zaman damgası zaman birimiyle, varlık etiketi
  içerikle sınırlıdır ve zayıf etiket çözünürlüğü bilerek düşürür.
- Kırk alışverişteki **12** bayat kopyanın **9**'u yakalanır, **3**'ünde damga değişimi
  kaçar ve aracı **taze** der; çözünürlük **0,7500**.
- Açık rejimin **5** yanlış kararının **3**'ü bu üç kopyadan gelir; kalan ikisi kişiye özel
  imden gelir ve tazelikle ilgisi yoktur.
- Üç rejim: `yalnız süre` **186**/**14**/**0** ve 0 tur, `damga` **195**/**5**/**0** ve 0
  tur, `her seferinde sor` **158**/**2**/**40** ve 40 tur. Sormak yanlışı düşürür, tur borcu
  doğurur.

#### Sonraki Adım

Bu konu boyunca aracı kararlarını hep aynı gerekçeyle aldı: **iletide yazanı okuyabiliyordu.**
Yöntemi okudu ve yinelenebilirliğe karar verdi, imi okudu ve paylaşılabilirliğe, damgayı
okudu ve tazeliğe. Yanıldığı yerlerde bile okuyabiliyordu; yanılma okumadan değil,
okuduğunun eksik olmasından geldi.

Sonraki konu bu koşulu kaldırır. **Ya ileti okunamıyorsa?** İki uç, aralarında geçen
iletiyi yalnız birbirlerinin anlayabileceği hâle getirirse aracının elinde ne kalır? Soru
kursun üçüncü iddiasına bağlanır: karar veren yalnız iki uç değildir ve iletiyi kapatmak
aradaki kararları uçlara taşır. Sonraki ders bu kapatmanın nasıl kurulduğunu ve kime neyi
ödettiğini ele alacak.

## Güvenli Taşıma

Simetrik ve asimetrik şifrelemenin rolleri, sertifika zincirinin doğrulanması, el sıkışmanın gidiş–dönüş bütçesi ve sertifikanın yaşam döngüsü.

### Simetrik ve Asimetrik Şifreleme

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/simetrik-ve-asimetrik-sifreleme

Anahtar modellerinin rolleri: asimetrik kurulumda, simetrik taşımada. Kurulum sayısının alışveriş başına düşen gidiş–dönüş maliyeti ve aracının kaybettiği görünürlük.

Buraya kadar sayılan her karar tek bir varsayıma dayanıyordu: aracı iletiyi okuyabiliyor.
Yöntemi, yolu, kişiye özel imini ve doğrulayıcıyı okuduğu için `yinelenebilir`,
`saklanabilir` ve `taze` kararlarını fazladan bir gidiş–dönüş olmadan alabiliyordu. Bu
varsayımın bir bedeli vardır ve bedeli sessizdir: iletiyi okuyabilen katman, yalnız
kurgudaki aracı değil, yolun üzerindeki **her** katmandır.

Bu dersin sorusu, iletiyi okunamaz kılan düzeneğin ne olduğu ve bunun kaça mal olduğudur.
Ölçü değişmiyor — hâlâ gidiş–dönüş ve karar sayıyoruz. Değişen, ölçülen şeyin artık bir alan
değil bir **rol** olması: hangi anahtar modeli hangi adımda çalışıyor ve o adım kaç tur
tutuyor.

#### İki Anahtar Modeli

**Simetrik şifreleme (symmetric encryption)** tek bir sır kullanır. Aynı sır iki tarafta
bulunur; gönderen onunla gövdeyi kapatır, alan aynı sırla açar. Maliyeti gövdenin
uzunluğuyla ölçeklenir ve tur maliyeti yoktur — sır zaten iki taraftadır. Tek sorusu şudur:
**sır iki tarafa nasıl geldi?**

Bu soru küçük görünür ama tek başına simetrik modeli bir ağ protokolü için yetersiz kılar.
Sır önceden elden verilmek zorundaysa, birbiriyle konuşmak isteyen her uç çiftinin ayrı bir
sırra ihtiyacı olur ve gereken sır sayısı uç sayısıyla değil **uç çifti sayısıyla** büyür.
Dahası, hiç karşılaşmamış iki uç hiçbir zaman konuşamaz. Ağın tanımı gereği uçlar birbirini
önceden tanımaz; taşıma güvenliğinin çözmesi gereken ilk sorun budur.

**Asimetrik şifreleme (asymmetric encryption)** bir çift kullanır. **Açık anahtar (public
key)** dağıtılır, **gizli anahtar (private key)** hiç gönderilmez. Çiftin işe yaraması
şuradan gelir: bir taraf kendi gizlisiyle karşının açık değerini birleştirdiğinde, karşı
taraf da kendi gizlisiyle onun açık değerini birleştirdiğinde **aynı sayı** çıkar. Bu sayı
hiçbir zaman yolda taşınmaz; iki uçta ayrı ayrı üretilir. Bu adıma **anahtar değişimi (key
exchange)**, çıkan sayıya **ortak sır (shared secret)** denir.

Kimlik, Erişim ve Kriptografi kursu bu iki modelin güvenlik özelliklerini kurdu: hangi
varsayım altında neyin kırılamaz sayıldığını, imzanın neyi kanıtladığını, anahtar
uzunluğunun ne anlama geldiğini. O kuram burada tekrarlanmıyor. Fark tek cümlede durur:
orada **güvenlik özelliği** ölçüldü, burada **tur ve karar** ölçülüyor.

#### Rollerin Sırası

İki model rakip değildir; sırayla çalışır.

```text
kurulum — asimetrik roller
  1. istemci -> sunucu : istemcinin açık değeri
  2. sunucu -> istemci : sunucunun açık değeri
  3. iki taraf da ortak sırra kendi başına varır (yolda hiçbir şey taşınmaz)

taşıma — simetrik rol
  4. istemci -> sunucu : ortak sırla kapatılmış gövde
  5. sunucu -> istemci : ortak sırla kapatılmış gövde
  6. 4-5 istenildiği kadar yinelenir; 1-3 yinelenmez
```

Bu döküm bir belirtim özeti değil, rollerin sırasıdır ve çalıştırılmaz. Dikkat edilecek
satır altıncısıdır: **kurulum bir kez, taşıma çok kez.**

#### Ders İçinde Modellenmiş Düzenek

Aşağıdaki düzenek **ders içinde modellenmiştir**. Gerçek bir anahtar değişimi değildir,
gerçek bir şifre değildir ve hiçbir güvenlik özelliği taşımaz: modülüs küçültülmüştür, gizli
sayılar dardır, simetrik dönüşüm bir oyuncaktır. Modellenen tek şey **ortaklığın nasıl
doğduğu** ve **hangi işin hangi adımda yapıldığıdır**.

```python
TOHUM = 20260809
ASAL = 2147483647          # ders içinde küçültülmüş modülüs
TABAN = 5


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % ASAL
        return d % n
    return r


def acik_deger(gizli: int) -> int:
    return pow(TABAN, gizli, ASAL)


def ortak_sir(kendi_gizli: int, karsi_acik: int) -> int:
    return pow(karsi_acik, kendi_gizli, ASAL)


def simetrik(veri: bytes, sir: int) -> bytes:
    r = uretec(sir)
    return bytes(b ^ r(251) for b in veri)


r = uretec(TOHUM)
iletiler = [bytes(r(251) for _ in range(180 + r(441))) for _ in range(40)]

istemci_gizli, sunucu_gizli = 1 + r(9973), 1 + r(9973)
istemci_sir = ortak_sir(istemci_gizli, acik_deger(sunucu_gizli))
sunucu_sir = ortak_sir(sunucu_gizli, acik_deger(istemci_gizli))

tasinan = [simetrik(i, istemci_sir) for i in iletiler]
cozulen = [simetrik(t, sunucu_sir) for t in tasinan]

print(f"ortak sır aynı: {istemci_sir == sunucu_sir}")
print(f"ileti {len(iletiler)}, gövde {sum(len(i) for i in iletiler)} bayt")
print(f"taşınan {sum(len(t) for t in tasinan)} bayt, çözülen özdeş: {cozulen == iletiler}")
```

```
ortak sır aynı: True
ileti 40, gövde 15767 bayt
taşınan 15767 bayt, çözülen özdeş: True
```

İki taraf da yalnız kendi gizlisini ve karşının açık değerini kullandı; gizliler hiç
gönderilmedi ve iki taraf aynı sayıya vardı. Gövde o sayıdan türetilen bir diziyle katlandı;
aynı dizi ikinci kez uygulandığında gövde geri geldi. Taşınan bayt sayısının gövdeyle aynı
kalması da tesadüf değil: simetrik rol veriyi **uzatmaz**, yalnız okunamaz kılar.

Gerçek bir taşımada bu düzeneğin her parçası daha ağırdır: modülüs çok daha büyük seçilir,
gizli sayılar çok daha geniş bir aralıktan çekilir, simetrik dönüşüm gövdeyi kapatmakla
kalmayıp değiştirilip değiştirilmediğini de bildirir. Bu seçimlerin gerekçesi Kimlik, Erişim
ve Kriptografi kursunun konusudur. Burada modelden alınan tek şey **iş bölümüdür**.

#### Kurulum Bir Kez, Taşıma Çok Kez

Kırk alışverişin tamamı aynı düzenekten geçirilirse, değişen tek şey **kaç kez kurulum
yapıldığıdır**. Aşağıdaki ölçüm bunu üç rejimde sayıyor: her iletiye bir kurulum, sekiz
bağlantıya bölünmüş kurulum, tek kalıcı bağlantı.

Ölçümün varsayımları:

- **GT1** — Şifrelemenin kendisi **ders içinde modellenmiştir**: modülüs küçültülmüş, gizli
  sayılar dar bir aralıktan çekilmiş, simetrik dönüşüm bir katlamadır. Gerçek bir algoritma
  çalıştırılmaz ve düzenek hiçbir güvenlik özelliği taşımaz.
- **GT2** — Gövde tek bir tohumdan üretilir: kırk ileti, her biri 180 ile 620 bayt arasında.
  Aynı gövde üç rejimde de kullanılır.
- **GT3** — Bir kurulum bir gidiş–dönüş sayılır; her kurulumda iki taraf **yeni** birer gizli
  çeker ve iki açık değer üretilir.
- **GT4** — Üç rejim karşılaştırılır: her iletide kurulum, sekiz bağlantı, tek kalıcı
  bağlantı. Bağlantı başına bir kurulum yapılır ve o kurulumdan çıkan sır, bağlantının bütün
  alışverişlerini kapsar.
- **GT5** — Kırk alışverişlik kümede ölçülebilen en küçük tur farkı bir kurulumdur, yani
  1/40 = 0,025; bundan küçük bir kazanç bu kümeyle savunulamaz.
- **GT6** — Rol maliyeti süreyle değil **işlem sayısıyla** ölçülür: üs alma kare-al-ve-çarp
  biçiminde açılıp modüler çarpmalar, simetrik dönüşümde katlamalar sayılır. Bu derste
  sayılan şey turdur; aracının karar tablosu ölçüme girmez.

```python
TOHUM = 20260809
ASAL = 2147483647
TABAN = 5
SAYAC = {"acik_deger": 0}


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % ASAL
        return d % n
    return r


def acik_deger(gizli: int) -> int:
    SAYAC["acik_deger"] += 1
    return pow(TABAN, gizli, ASAL)


def kurulum(rk) -> int:
    istemci_gizli, sunucu_gizli = 1 + rk(9973), 1 + rk(9973)
    acik_deger(istemci_gizli)                       # sunucuya giden değer
    return pow(acik_deger(sunucu_gizli), istemci_gizli, ASAL)


def simetrik(veri: bytes, sir: int) -> bytes:
    r = uretec(sir)
    return bytes(b ^ r(251) for b in veri)


r = uretec(TOHUM)
iletiler = [bytes(r(251) for _ in range(180 + r(441))) for _ in range(40)]

print(f"{'rejim':<22s} {'kurulum':>8s} {'açık değer':>11s} {'simetrik bayt':>14s} "
      f"{'alışveriş başına tur':>21s} {'sır başına alışveriş':>21s}")
for ad, baglanti in (("her iletide kurulum", 40), ("sekiz bağlantı", 8),
                     ("tek kalıcı bağlantı", 1)):
    SAYAC["acik_deger"] = 0
    rk, bayt = uretec(TOHUM + 1), 0
    sirlar = [kurulum(rk) for _ in range(baglanti)]
    for sira, ileti in enumerate(iletiler):
        bayt += len(simetrik(ileti, sirlar[sira % baglanti]))
    print(f"{ad:<22s} {baglanti:8d} {SAYAC['acik_deger']:11d} {bayt:14d} "
          f"{baglanti / len(iletiler):21.3f} {len(iletiler) / baglanti:21.1f}")
```

```
rejim                   kurulum  açık değer  simetrik bayt  alışveriş başına tur  sır başına alışveriş
her iletide kurulum          40          80          15767                 1.000                   1.0
sekiz bağlantı                8          16          15767                 0.200                   5.0
tek kalıcı bağlantı           1           2          15767                 0.025                  40.0
```

Üç satır arasında **simetrik bayt değişmiyor**: gövde her rejimde aynı, 15767 bayt. Değişen
tek şey kurulum sayısı ve onun doğurduğu turdur. Her iletide kurulum yapan rejimde alışveriş
başına **1,000** ek gidiş–dönüş düşer — her ileti kendi kurulumunu öder. Sekiz bağlantıda bu
**0,200**, tek kalıcı bağlantıda **0,025** olur.

Kırk kat fark tek bir tasarım kararından gelir. Asimetrik rolün maliyeti gövdeyle değil
**kurulum sayısıyla** ölçeklenir; simetrik rolün maliyeti kurulum sayısıyla değil
**gövdeyle** ölçeklenir. İki modelin neden birlikte kullanıldığının tamamı budur: pahalı
olan seyrek çalışır, ucuz olan sık.

Bu kümenin çözünürlüğü de burada okunur. Kırk alışverişlik bir kümede ölçülebilen en küçük
tur farkı **bir kurulumdur**, yani 1/40 = 0,025. Bundan küçük bir kazanç iddia edilemez.

Son sütun bedava olmadığını gösteriyor. Kurulum seyrekleştikçe **bir sırra bağlanan alışveriş
sayısı** artar: her iletide kurulumda bir sır bir alışverişi kapsar, sekiz bağlantıda beşini,
tek kalıcı bağlantıda kırkını birden. Tur kazancı ile yalıtım aynı kolu çekiyor ve ters
yönlere gidiyor. Kurulumun her yinelenişinde iki tarafın **yeni** birer gizli çekmesi de bu
yüzden anlamlıdır: yeni gizli, kapsamı yeniden bir bağlantıyla sınırlar. Gizliyi yinelemek —
aynı gizliyi bütün kurulumlarda kullanmak — tabloda hiçbir turu kazandırmaz ama son sütunu
sessizce kırka çıkarır.

#### Neden Taşıma Simetriktir

Rollerin sırası bir alışkanlık değil, bir hesabın sonucudur. Modellenmiş düzenekte iki işin
maliyeti aynı birimle sayılabilir: asimetrik rol **modüler çarpma** yapar, simetrik rol
**katlama**. Üs alma işlemini kare-al-ve-çarp biçiminde açıp her çarpmayı saymak, sıranın
nereden geldiğini gösterir. Aynı gövde iki rolle de taşınır ve ikisi de geri açılır.

```python
TOHUM = 20260809
ASAL = 2147483647
TABAN = 5
ACIK_US = 1223
GIZLI_US = pow(ACIK_US, -1, ASAL - 1)


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % ASAL
        return d % n
    return r


def us_al(taban: int, us: int, sayac: list[int]) -> int:
    """Kare-al-ve-çarp: her modüler çarpma sayaca düşer."""
    sonuc, t = 1, taban % ASAL
    while us > 0:
        if us % 2 == 1:
            sonuc = (sonuc * t) % ASAL
            sayac[0] += 1
        t = (t * t) % ASAL
        sayac[0] += 1
        us //= 2
    return sonuc


def katla(veri: bytes, sir: int, sayac: list[int]) -> bytes:
    r = uretec(sir)
    sayac[0] += len(veri)
    return bytes(b ^ r(251) for b in veri)


r = uretec(TOHUM)
govde = b"".join(bytes(r(251) for _ in range(180 + r(441))) for _ in range(40))

kurulum = [0]
istemci_gizli, sunucu_gizli = 1 + r(9973), 1 + r(9973)
sunucu_acik = us_al(TABAN, sunucu_gizli, kurulum)
us_al(TABAN, istemci_gizli, kurulum)
sir = us_al(sunucu_acik, istemci_gizli, kurulum)

asimetrik = [0]
kapali = [us_al(b, ACIK_US, asimetrik) for b in govde]
acilan = bytes(us_al(x, GIZLI_US, asimetrik) for x in kapali)

simetrik = [0]
s_acilan = katla(katla(govde, sir, simetrik), sir, simetrik)

print(f"{'iş':<34s} {'modüler çarpma':>15s} {'katlama':>9s}")
print(f"{'kurulum (üç üs alma)':<34s} {kurulum[0]:15d} {0:9d}")
print(f"{'gövdeyi asimetrik taşımak':<34s} {asimetrik[0]:15d} {0:9d}")
print(f"{'gövdeyi simetrik taşımak':<34s} {0:15d} {simetrik[0]:9d}")
print()
print(f"gövde {len(govde)} bayt, asimetrik geri geldi: {acilan == govde}, "
      f"simetrik geri geldi: {s_acilan == govde}")
print(f"asimetrik taşıma kuruluma göre {asimetrik[0] / kurulum[0]:.0f} kat")
```

```
iş                                  modüler çarpma   katlama
kurulum (üç üs alma)                            54         0
gövdeyi asimetrik taşımak                   930253         0
gövdeyi simetrik taşımak                         0     31534

gövde 15767 bayt, asimetrik geri geldi: True, simetrik geri geldi: True
asimetrik taşıma kuruluma göre 17227 kat
```

Kurulum üç üs alma tutar ve toplam **54** modüler çarpma yapar. Gövde ne kadar büyürse
büyüsün bu sayı değişmez, çünkü asimetrik rol gövdeye değil **sabit boyutlu bir sayıya**
dokunur. Aynı gövdeyi baştan sona asimetrik rolle taşımak **930253** modüler çarpma tutar:
kurulumun **17227 katı**. Simetrik rol aynı işi **31534** katlamayla bitirir ve tek bir
modüler çarpma yapmaz.

Önemli olan sayıların büyüklüğü değil, **neye bağlı** oldukları. Kurulumun maliyeti gövdeden
bağımsızdır; asimetrik taşımanın maliyeti gövdeyle doğrusal büyür ve her baytı pahalı bir
işlemden geçirir; simetrik taşımanın maliyeti de gövdeyle doğrusal büyür ama bayt başına
yaptığı iş ucuzdur. Rollerin sırası bu üç eğrinin karşılaştırmasından çıkar: pahalı olan
sabit kalabildiği yerde, ucuz olan büyüyebildiği yerde çalışır.

Bir ayrıntı daha vardır. Kurulumdan çıkan ortak sır gövdeye doğrudan uygulanmaz; ondan
türetilen ayrı bir **oturum anahtarı (session key)** kullanılır ve genellikle her yön için
ayrı bir anahtar türetilir. Türetme yerel bir işlemdir, ileti göndermez, tur eklemez.
Kazandırdığı şey iki yönün ve iki oturumun birbirinden ayrılmasıdır. Tur bütçesi açısından
sonuç şudur: türetilen anahtar sayısı artsa bile **kurulumun turu sabit kalır**.

#### Aracının Gördüğü ve Görmediği

Gövde kapandığında aracı gövdeden karar alamaz. Bu dersin modellediği dönüşüm yalnız gövdeyi
kapattı; sonraki iki dersin konusu olan **taşıma zarfı (transport envelope)** başlık
alanlarını da içine alır ve aracının okuyabildiği kümeyi daha da daraltır. O daraltmanın
karar tablosuna ne yaptığı el sıkışma dersinde sayılacak. Burada sayılan yalnız turdur.

Kapalı bir taşımada aracının hâlâ gördüğü şeyler vardır ve her birinin bir daraltması
bulunur:

- **Konak adı.** Aracı iletiyi doğru sunucuya yönlendirebilmek için bir ada ihtiyaç duyar.
  *Daraltma:* ad da zarfa alınırsa aracı yönlendiremez; bu bir kayıp değil takastır — karar
  aracıdan alınır, uca verilir.
- **İleti uzunluğu.** Simetrik rol veriyi uzatmadığı için taşınan bayt, gövdenin baytıdır.
  *Daraltma:* gövdeye dolgu eklenirse uzunluk kaba bir bant hâline gelir ve ayırt ediciliği
  düşer.
- **Tur deseni.** Kaç ileti, hangi aralıkla gidiyor. *Daraltma:* alışverişler tek bir kalıcı
  bağlantıda birleştirilirse desen bir bağlantının içine gömülür.
- **Hedef adres ve bağlantı noktası.** Taşıma katmanının başlıkları kapanan gövdenin dışında
  kalır; aracı iletiyi iletebilmek için onları okumak zorundadır. *Daraltma:* aynı adres arkasında
  çok sayıda uç toplanırsa adres tek bir ucu değil bir kümeyi gösterir ve ayırt ediciliği düşer.

Bir de **yapılandırma kusuru** olarak okunması gereken bir seçim vardır: kurulum turunu
tamamen atlayıp ortak sırrı önceden paylaşılmış sabit bir değere bağlamak. Kazanç açıktır —
tabloda 0,025'lik satır bile ortadan kalkar. Yanlış olan şudur: her oturum aynı sırra
bağlanır, oturumlar birbirinden ayrılamaz ve sırrın tek bir kez elden çıkması geçmiş
alışverişleri de kapsar. Bu dersin söylediği, neyin yanlış olduğudur; bunun nasıl
kullanılacağı bu kursun konusu değildir.

#### Özet

- Simetrik model tek sır kullanır ve maliyeti gövdeyle ölçeklenir; asimetrik model bir çift
  kullanır ve maliyeti kurulum sayısıyla ölçeklenir.
- Anahtar değişimi, iki tarafın kendi gizlisini göndermeden aynı ortak sırra varmasıdır;
  ortak sır yolda hiç taşınmaz.
- Ders içinde modellenmiş düzenekte iki taraf aynı sayıya vardı ve 15767 baytlık gövde
  dönüşümden geçip özdeş olarak geri geldi; düzenek güvenlik özelliği taşımaz.
- Kırk alışverişte alışveriş başına ek gidiş–dönüş, her iletide kurulumda 1,000; sekiz
  bağlantıda 0,200; tek kalıcı bağlantıda 0,025 çıkar — simetrik bayt üçünde de aynıdır.
- Kapalı taşımada aracının gördüğü konak adı, ileti uzunluğu ve tur deseninin her birinin bir
  daraltması vardır; ortak sırrı sabit bir değere bağlamak ise bir yapılandırma kusurudur.

#### Sonraki Adım

Kurulum bir sayı üretti ve iki taraf o sayıda anlaştı. Ama anlaşılan şey **sırdır, kimlik
değildir**: ortak sır, karşı ucun kim olduğu hakkında hiçbir şey söylemez. Açık değeri
gönderen taraf ölçüm istasyonu da olabilir, o adı kullanan başka bir uç da. İstemcinin
elindeki tek dayanak yine iletide yazandır — ve iletide yazanı doğrulayan hiçbir şey yoktur.
Sonraki ders, bir adın bir açık anahtara nasıl bağlandığını ve istemcinin bu bağı doğrularken
kaç kez soru sorduğunu ölçecek.

### Sertifikalar ve Güven Zinciri

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/sertifikalar-ve-guven-zinciri

Bir adın bir açık anahtara bağlanması, zincirin güven çıpasına kadar yerel yürütülmesi ve bu kararın gidiş–dönüş bütçesine hiç dokunmaması.

Önceki ders kurulumun bir ortak sır ürettiğini gösterdi ve o sırrın kaç tura mal olduğunu
saydı. Üretilen şeyin adı sırdı; **kimlik değildi.** İstemci karşı ucun açık değerini aldı,
onunla ortak bir sayıya vardı ve gövdeyi o sayıyla kapattı — ama karşı ucun ölçüm istasyonu
olduğunu söyleyen hiçbir şey yoktu. Açık değeri gönderen taraf `istasyon.ornek` de olabilir,
o adı kullanan başka bir uç da.

Bu dersin sorusu, bir **adın** bir **açık anahtara** nasıl bağlandığı ve istemcinin bu bağı
doğrularken kaç kez soru sorduğudur. İkinci yarısı kursun eksenidir: zincir doğrulaması
**fazladan bir gidiş–dönüş olmadan alınan bir karardır** ve zincir uzasa da bu değişmez.

#### Sertifikanın Bildirdiği

Sertifika, bir adı bir açık anahtara bağlayan ve bu bağın altına başka birinin imzasını koyan
bir bildirimdir. Alanları şunu söyler:

```text
sertifika
  konu         : istasyon.ornek            <- ad kimin
  açık anahtar : <konunun açık anahtarı>   <- o adla hangi anahtar konuşur
  veren        : ara-1.ornek               <- bağı kim onayladı
  geçerlilik   : başlangıç .. bitiş        <- bağ ne zaman aralığında geçerli
  imza         : verenin gizli anahtarıyla üretilmiş
```

Bu döküm çalıştırılmaz; alanların ne bildirdiğini gösterir. Sertifikayı çıkaran kuruluşa
**sertifika otoritesi (certificate authority)** denir.

Aracının konumu buradan tanıdıktır: yine iletide yazana bakılıyor. Fark tek bir alandadır —
burada iletide yazanı **doğrulayan** bir şey vardır: imza. Kursun geri kalanında "aracı
bildirime inanır" derken kastedilen şey, bu doğrulamanın olmamasıdır.

#### İmza Ne Kanıtlar

Aşağıdaki imza düzeneği **ders içinde modellenmiştir.** Gerçek bir imza şeması değildir ve
hiçbir güvenlik özelliği taşımaz: modülüs küçültülmüştür, özet işlevi bir oyuncaktır ve bu
modelde açık anahtardan gizli anahtar geri elde edilebilir. Şemaların güvenlik gerekçesi
Kimlik, Erişim ve Kriptografi kursunun konusudur. Burada modelden alınan tek şey **imzanın
hangi alanların üzerinde durduğudur.**

```python
from math import gcd

TOHUM = 20260809
ASAL = 2147483647          # ders içinde küçültülmüş modülüs


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % ASAL
        return d % n
    return r


def ozet(metin: str) -> int:
    h = 2166136261
    for b in metin.encode():
        h = (h * 16777619 + b) % ASAL
    return h


def anahtar_cifti(r) -> tuple[int, int]:
    while True:
        gizli = 3 + r(ASAL - 4)
        if gcd(gizli, ASAL - 1) == 1:
            return gizli, pow(gizli, -1, ASAL - 1)


def imzala(konu: str, acik: int, veren: str, veren_gizli: int, gecerli: bool) -> dict:
    s = {"konu": konu, "acik": acik, "veren": veren, "gecerli": gecerli}
    s["imza"] = pow(ozet(f"{konu}|{acik}|{veren}|{gecerli}"), veren_gizli, ASAL)
    return s


def imza_dogru(s: dict, veren_acik: int) -> bool:
    return pow(s["imza"], veren_acik, ASAL) == ozet(
        f"{s['konu']}|{s['acik']}|{s['veren']}|{s['gecerli']}")


r = uretec(TOHUM)
cipa_gizli, cipa_acik = anahtar_cifti(r)
yaprak_gizli, yaprak_acik = anahtar_cifti(r)
yaprak = imzala("istasyon.ornek", yaprak_acik, "cipa.ornek", cipa_gizli, True)

print(f"konu {yaprak['konu']}, veren {yaprak['veren']}")
print(f"imza doğru mu: {imza_dogru(yaprak, cipa_acik)}")
print(f"konusu değiştirilmiş kopya: {imza_dogru(dict(yaprak, konu='baska.ornek'), cipa_acik)}")
print(f"anahtarı değiştirilmiş kopya: {imza_dogru(dict(yaprak, acik=7), cipa_acik)}")
```

```
konu istasyon.ornek, veren cipa.ornek
imza doğru mu: True
konusu değiştirilmiş kopya: False
anahtarı değiştirilmiş kopya: False
```

İmza konunun, açık anahtarın, verenin ve geçerlilik alanının **üzerinde** durur; alanlardan
biri değişirse doğrulama düşer. Gerçek bir X.509 sertifikasında imza, belgenin imzalanacak
bölümünün **tamamını** kapsar — seri numarası ve aşağıda konu edilecek alternatif ad alanı
gibi uzantılar dahil; modeldeki dört alan bu bütünün bir örneğidir. Bunun sonucu ince ama
önemlidir: imza sertifikayı değiştirilmez kılmaz, **değiştirildiğini belli eder.**

İmzanın doğrudan belgenin üzerinde değil, belgenin **özeti** üzerinde durduğu da eklenmelidir.
Bu, imzalanacak şeyin uzunluğunu sabitler ve imza işini belgenin boyundan bağımsız kılar —
önceki dersteki iş bölümünün aynısı, asimetrik rol yine sabit boyutlu bir sayıya dokunur.
Karşılığında yeni bir koşul doğar: iki farklı belgenin aynı özete düşmemesi gerekir. Bu
koşulun hangi varsayımla sağlandığı Kimlik, Erişim ve Kriptografi kursunun konusudur.

İmzanın kanıtlamadıkları da sayılmalıdır. İmza, verenin bu bağı onayladığını gösterir; verenin
onaylamaya **yetkili** olduğunu göstermez. Konunun o anda hâlâ var olduğunu göstermez.
Konunun gizli anahtarının başkasının eline geçmediğini de göstermez. Bunların hepsi
sertifikanın **dışında** olup bitmiş şeylerdir ve imzalanmış bir belgeye sonradan yazılamaz.

#### Zincir ve Güven Çıpası

İmzayı doğrulamak için verenin açık anahtarı gerekir. O anahtarın verene ait olduğunu ne
söyler? Başka bir sertifika. Bu soru kendini yineler ve **sertifika zinciri** doğar: en altta
konuyu taşıyan **yaprak sertifika**, üstünde bir ya da birkaç **ara sertifika**, en üstte
zincirin durduğu yer.

Zincir bir yerde durmak zorundadır ve durduğu yer bir imzayla değil, bir **yerleşimle**
belirlenir. İstemcinin elinde önceden bulunan, başka hiçbir şeye karşı doğrulanmayan
sertifikaya **güven çıpası (trust anchor)** denir. Çıpa **kendinden imzalı (self-signed)**
olabilir, çoğunlukla öyledir; ama onu güvenilir yapan kendi imzası değildir — kendi imzası
hiçbir şey söylemez. Onu güvenilir yapan **istemcinin elinde olmasıdır.**

Bu ayrım kursun eksenine doğrudan bağlanır. Çıpa istemcinin yanındadır, ağın ucunda değil.
Yani zincirin en üst basamağı için bile kimseye soru sorulmaz.

Zinciri kim taşır sorusunun yanıtı da tur bütçesini belirler. Yaprak ve ara sertifikaları
**sunucu gönderir**; çıpayı **istemci tutar**. İstemcinin zinciri toplamak için ayrıca bir
istek yapması gerekmez, çünkü zincir zaten sunumun içinde gelir. Sunucunun ara sertifikaları
eksik göndermesi ise istemciyi tur harcamaya değil, doğrudan redde iter: elindeki parçalar
çıpaya varmıyorsa istemci kararını verir.

#### Adın Eşleştirilmesi

Zincir tutarlı olsa bile karar bir eşleştirmeye daha bağlıdır: istemcinin gitmek istediği ad,
sertifikanın bildirdiği adlardan birini karşılamalıdır. X.509 sertifikaları bu adları
alternatif ad alanında taşır; bir sertifika birden çok ad bildirebilir ve joker biçim
(`*.istasyon.ornek` gibi) tek bir seviyeyi karşılar.

Eşleştirmenin iki özelliği kursun eksenine bağlanır. Birincisi, eşleştirme de **yereldir**:
istemci elindeki adı elindeki listeyle karşılaştırır, kimseye sormaz. İkincisi ve daha
incesi, karşılaştırmanın iki ucu **ayrı kaynaklardan** gelmek zorundadır. İstenen ad
istemcinin kendi isteğinden gelir, bildirilen ad sertifikadan. Ad sertifikadan okunup yine
sertifikayla karşılaştırılırsa denetim her zaman geçer ve hiçbir şey ölçmez — denetimin
anlamı, iki ucun bağımsızlığındadır.

#### Zincir Doğrulaması Tur İstemez

Kırk sertifika sunumu ölçülüyor. Zincir uzunlukları bir ile beş arasında değişiyor; kimi
sunumun bir halkasının imzası bozuk, kiminin zinciri istemcinin çıpalarına varmıyor, kiminin
yaprak konusu istenen adı karşılamıyor, kiminin geçerlilik alanı kapalı. Kâhin — kurguyu biz
ürettiğimiz için bilinen gerçek — her sunum için kabulün doğru olup olmadığını söylüyor. Her
sunum tek bir karar taşıyor, yani küme 40 karardır ve çözünürlüğü 1/40 = 0,025'tir.

Ölçümün varsayımları:

- **GT7** — İmza şeması **ders içinde modellenmiştir**: modülüs küçültülmüş, özet işlevi bir
  oyuncaktır ve bu modelde açık anahtardan gizli anahtar geri elde edilebilir. Ölçülen şey
  imzanın gücü değil, doğrulamanın **nerede** yapıldığıdır.
- **GT8** — Kırk sunum tek bir tohumdan üretilir; zincir uzunluğu bir ile beş arasında
  çekilir ve her sunuma en çok bir kusur yazılır: bozuk imza, çıpaya varmayan zincir, istenen
  adı karşılamayan konu, kapanmış geçerlilik alanı ya da iptal.
- **GT9** — Kâhin kurguyu biz ürettiğimiz için bilinir: bir sunumun kabulü ancak kusuru
  yoksa doğrudur. **İptal kusuru zincirin içine yazılmaz** — o zincirler baştan sona
  tutarlıdır ve doğrulama iptali göremez.
- **GT10** — Yaprak ve ara sertifikaları sunucu gönderir, güven çıpasını istemci tutar;
  zinciri toplamak için ayrıca istek yapılmaz. Çıpa kümesi tektir.
- **GT11** — Üç rejim karşılaştırılır: yerel çıpayla yürüme, her halkayı verenine sorma
  (halka başına bir gidiş–dönüş), doğrulamayı hiç yapmama. Yürüyüş **erken çıkar**: bozuk bir
  halkada durur, bu yüzden imza doğrulaması sayısı halka sayısına eşit olmaz.
- **GT12** — Ölçülen karar aracının değil **istemcinindir**; aracı bu tabloya girmez. Sayılan
  şey bir süre değil, doğrulama adımı ve gidiş–dönüştür.

```python
from math import gcd

TOHUM = 20260809
ASAL = 2147483647
KUSURLAR = ("yok", "yok", "yok", "yok", "yok", "yok", "yok",
            "imza", "cipa", "ad", "sure", "iptal", "iptal")


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % ASAL
        return d % n
    return r


def ozet(metin: str) -> int:
    h = 2166136261
    for b in metin.encode():
        h = (h * 16777619 + b) % ASAL
    return h


def anahtar_cifti(r) -> tuple[int, int]:
    while True:
        gizli = 3 + r(ASAL - 4)
        if gcd(gizli, ASAL - 1) == 1:
            return gizli, pow(gizli, -1, ASAL - 1)


def imzala(konu: str, acik: int, veren: str, veren_gizli: int, gecerli: bool) -> dict:
    s = {"konu": konu, "acik": acik, "veren": veren, "gecerli": gecerli}
    s["imza"] = pow(ozet(f"{konu}|{acik}|{veren}|{gecerli}"), veren_gizli, ASAL)
    return s


def imza_dogru(s: dict, veren_acik: int) -> bool:
    return pow(s["imza"], veren_acik, ASAL) == ozet(
        f"{s['konu']}|{s['acik']}|{s['veren']}|{s['gecerli']}")


def zincir_uret(uzunluk: int, ad: str, r, cipa_gizli: int, gecerli: bool) -> list[dict]:
    ust_gizli, ust_ad, zincir = cipa_gizli, "cipa.ornek", []
    for basamak in range(uzunluk, 0, -1):
        gizli, acik = anahtar_cifti(r)
        konu = ad if basamak == 1 else f"ara-{basamak - 1}.ornek"
        zincir.append(imzala(konu, acik, ust_ad, ust_gizli, gecerli or basamak > 1))
        ust_gizli, ust_ad = gizli, konu
    return zincir


def zincir_dogrula(zincir: list[dict], ad: str, cipalar: dict, sayac: dict) -> bool:
    ust_acik = cipalar.get(zincir[0]["veren"])
    if ust_acik is None:
        return False
    for s in zincir:
        sayac["imza"] += 1
        if not imza_dogru(s, ust_acik) or not s["gecerli"]:
            return False
        ust_acik = s["acik"]
    return zincir[-1]["konu"] == ad


r = uretec(TOHUM)
cipa_gizli, cipa_acik = anahtar_cifti(r)
CIPALAR = {"cipa.ornek": cipa_acik}

sunumlar = []
for no in range(40):
    uzunluk, kusur = 1 + r(5), KUSURLAR[r(13)]
    ad = "baska.ornek" if kusur == "ad" else "istasyon.ornek"
    zincir = zincir_uret(uzunluk, ad, r, cipa_gizli, kusur != "sure")
    if kusur == "imza":
        zincir[-1] = dict(zincir[-1], imza=(zincir[-1]["imza"] + 1) % ASAL)
    elif kusur == "cipa":
        zincir[0] = dict(zincir[0], veren="bilinmeyen.ornek")
    sunumlar.append({"uzunluk": uzunluk, "kusur": kusur, "zincir": zincir})


def olc(sunumlar, tur_basina, dogrula=True):
    sayac, dogru, yanlis, tur = {"imza": 0}, 0, 0, 0
    for s in sunumlar:
        verilen = (zincir_dogrula(s["zincir"], "istasyon.ornek", CIPALAR, sayac)
                   if dogrula else True)
        dogru += verilen == (s["kusur"] == "yok")
        yanlis += verilen != (s["kusur"] == "yok")
        tur += tur_basina * s["uzunluk"]
    return dogru, yanlis, tur, sayac["imza"]


print(f"{'rejim':<26s} {'doğru':>6s} {'yanlış':>7s} {'gidiş–dönüş':>12s} "
      f"{'imza doğrulaması':>17s}")
for ad_rejim, tur_basina, dgr in (("yerel çıpa", 0, True),
                                  ("her halkayı uzaktan sorma", 1, True),
                                  ("doğrulamayı atlama", 0, False)):
    d, y, t, i = olc(sunumlar, tur_basina, dgr)
    print(f"{ad_rejim:<26s} {d:6d} {y:7d} {t:12d} {i:17d}")

print()
print(f"{'zincir uzunluğu':>15s} {'sunum':>6s} {'imza doğrulaması':>17s} "
      f"{'gidiş–dönüş':>12s}")
for u in range(1, 6):
    esler = [s for s in sunumlar if s["uzunluk"] == u]
    print(f"{u:15d} {len(esler):6d} {olc(esler, 0)[3]:17d} {0:12d}")

print()
kusurlar = {k: sum(s["kusur"] == k for s in sunumlar) for k in
            ("yok", "imza", "cipa", "ad", "sure", "iptal")}
print(f"sunum {len(sunumlar)}, karar {len(sunumlar)}, "
      f"halka {sum(s['uzunluk'] for s in sunumlar)}, kusur {kusurlar}")
```

```
rejim                       doğru  yanlış  gidiş–dönüş  imza doğrulaması
yerel çıpa                     33       7            0                98
her halkayı uzaktan sorma      33       7          111                98
doğrulamayı atlama             17      23            0                 0

zincir uzunluğu  sunum  imza doğrulaması  gidiş–dönüş
              1      9                 9            0
              2     10                18            0
              3     10                24            0
              4      3                12            0
              5      8                35            0

sunum 40, karar 40, halka 111, kusur {'yok': 17, 'imza': 3, 'cipa': 4, 'ad': 5, 'sure': 4, 'iptal': 7}
```

İlk iki satır kursun eksenini tek başına ödüyor. Yerel çıpayla yürüyen istemci **33 kararı
doğru**, 7'sini yanlış aldı ve bunu **sıfır gidiş–dönüşle** yaptı. Her halkayı verenine soran
istemci **111 gidiş–dönüş** harcadı ve **aynı** 33 doğru, 7 yanlış tablosuna vardı. Sormak
tek bir kararı değiştirmedi; yalnız 111 tur ekledi.

İkinci tabloda gidiş–dönüş sütunu baştan sona sıfırdır. Zincir bir halkayken de beş
halkayken de istemci kimseye sormaz; artan tek şey **yerel iştir** — imza doğrulaması 9'dan
35'e çıkar. Yerel iş ile tur arasındaki fark budur: biri istemcinin işlemcisinde, öbürü ağın
üzerinde ödenir ve ikisi aynı birime çevrilemez.

Toplam imza doğrulaması 98, halka sayısı 111. Aradaki fark bir tutarsızlık değil, yürüyüşün
kendisidir: çıpaya varmayan dört zincirde hiçbir imza doğrulanmaz — istemci daha ilk adımda
elinde çıpa olmadığını görür — ve bozuk bir halkaya rastlandığında yürüyüş orada durur.
Zincir doğrulaması **erken çıkan** bir işlemdir.

#### Zincirin Söylemediği

Yanlış çıkan 7 kararın hepsi aynı kusurdan gelir ve tablonun son satırında adı yazılıdır:
`iptal` 7. Bu yedi sunumda zincir baştan sona tutarlıdır — imzalar birbirini tutar, ad
istenendir, geçerlilik alanı açıktır — ama sertifika artık geçerli sayılmamaktadır.

Ayrım şudur: zincir doğrulaması belgenin **iç tutarlılığını** ölçer ve ölçtüğü her şey
belgenin **içinde** yazılıdır. Bu yüzden yerel yürüyebilir ve bu yüzden tur istemez. İptal
ise belgenin **dışında** olan biteni bildirir ve belgeye sonradan yazılamaz, çünkü belge
imzalandığı anda donar. Zincir sormadan doğrulanır; iptal sorulmadan bilinemez.

Bu yedi karar bir **tur borcudur**. Borcun nasıl ödendiği, ödemenin kaç tura mal olduğu ve
ödememenin ne bıraktığı bu konunun son dersinde sayılacak.

#### Yapılandırma Kusurları ve Daraltmalar

Tablonun üçüncü satırı bir kusurun bedelidir. Zincir doğrulamasını hiç yapmayan istemci
sıfır tur ve sıfır imza doğrulamasıyla kurtulur; ödediği şey, doğru karar sayısının **33'ten
17'ye** inmesidir. Yanlış olan budur — kazanılan iş yereldir ve ucuzdur, kaybedilen ise
kararın kendisidir. Bunun nasıl yapıldığı bu kursun konusu değildir.

Doğrulamanın parçaları da ayrı ayrı sayılmalı ve her birinin yanına daraltması yazılmalıdır:

- **Ad denetimi.** Ölçümde 5 sunum yalnız bu denetim yüzünden reddedildi: zincirleri
  kusursuzdu, konuları istenen adı karşılamıyordu. *Daraltma:* ad denetimi yapılandırmayla
  açılıp kapanan bir seçenek değil, doğrulama yolunun ayrılmaz adımı olarak kurulursa
  kapatılamaz.
- **Geçerlilik denetimi.** Ölçümde 4 sunum yalnız bu yüzden reddedildi. *Daraltma:* geçerlilik
  aralıkları kısa tutulursa yanlış bir saatin açtığı pencere de kısalır.
- **Çıpa kümesinin genişliği.** İstemcinin elindeki her çıpa, o çıpadan inen **her** zinciri
  kabul edilebilir kılar. *Daraltma:* çıpa kümesi gerçekten kullanılan uçlara göre daraltılırsa
  kabul edilebilir zincir kümesi de aynı oranda daralır.
- **Zincirin ara halkaları.** Ölçümde 4 sunum çıpaya hiç varmadı ve bu yüzden reddedildi;
  sunucunun ara sertifikaları eksik göndermesi de aynı sonucu doğurur. *Daraltma:* eksik
  halka istemcide değil sunucu yapılandırmasında kapatılır — istemcinin çıpa kümesini
  genişleterek değil.

Son madde bir alışkanlığı da açıklar: "sertifika hatası" görünen durumların çoğu bir saldırı
değil, bir **yapılandırma kusurudur**. Kusurun nerede olduğu, hangi denetimin düştüğüne
bakılarak ayrılır.

#### Özet

- Sertifika bir adı bir açık anahtara bağlar ve bağın altına verenin imzasını koyar; imza
  konu, anahtar, veren ve geçerlilik alanlarının üzerinde durur.
- İmza belgeyi değiştirilmez kılmaz, değiştirildiğini belli eder; verenin yetkili olduğunu,
  konunun hâlâ var olduğunu ve anahtarın elde kaldığını göstermez.
- Zincir güven çıpasında durur; çıpayı güvenilir yapan kendi imzası değil, istemcinin elinde
  bulunmasıdır.
- Kırk sunumda yerel çıpayla yürüyen istemci 33 doğru, 7 yanlış kararı **0 gidiş–dönüşle**
  aldı; her halkayı soran istemci **111 gidiş–dönüş** harcayıp aynı tabloya vardı.
- Zincir uzunluğu 1'den 5'e çıkarken yerel imza doğrulaması 9'dan 35'e çıktı, gidiş–dönüş her
  satırda 0 kaldı.
- Yanlış çıkan 7 kararın tamamı iptalden gelir: zincir belgenin içini ölçer, iptal belgenin
  dışında olan bitendir.

#### Sonraki Adım

Zincir doğrulaması hiç tur istemedi. Ama bu doğrulama havada durmaz: belirli bir bağlantının
içinde, belirli bir anda yapılır ve sertifikanın istemciye ulaşması için önce bir alışverişin
başlaması gerekir. O alışverişin kendi bütçesi vardır — kaç ileti gidip geliyor, sertifika
hangi turda geliyor, gövde hangi turdan sonra taşınmaya başlıyor. Sonraki ders bu bütçeyi
sayacak ve bütçenin kapanmasıyla birlikte aracının karar tablosuna ne olduğunu ölçecek.

### TLS El Sıkışması

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/tls-el-sikismasi

El sıkışmanın gidiş–dönüş bütçesi, oturum sürdürmenin kazandırdığı tur ve taşıma zarfı kapandığında aracının kaybettiği 160 karar.

Zincir doğrulaması hiç tur istemedi, ama havada da yapılmadı. Sertifikanın istemcinin eline
geçmesi için önce bir alışverişin başlaması, sunucunun zinciri sunması ve iki tarafın ortak
sırra varması gerekiyordu. **TLS (Transport Layer Security)** el sıkışması bu üç işi tek bir
kurulum içinde yapar.

Bu dersin sorusu iki katlıdır. Birincisi bütçedir: kurulum kaç gidiş–dönüş tutar, bu bütçe
kim tarafından ödenir ve tekrar tekrar ödenmesi nasıl önlenir. İkincisi kursun asıl
konusudur: kurulum tamamlandığında ileti bir **taşıma zarfı (transport envelope)** içine
girer ve aracı artık iletide yazanı okuyamaz. Kurs boyunca sayılan karar tablosuna bunun ne
yaptığı burada ölçülüyor.

#### El Sıkışmanın Taşıdıkları

El sıkışma, üzerinde anlaşılması gereken her şeyi az sayıda iletide toplar.

```text
tam kurulum
  istemci -> sunucu : selam, desteklenen takımlar, istemcinin anahtar payı
  sunucu  -> istemci: selam, seçilen takım, sunucunun anahtar payı,
                      sertifika zinciri, bitirme
  istemci -> sunucu : bitirme, ardından uygulama verisi

oturum sürdürme
  istemci -> sunucu : selam, önceki oturumun sürdürme kimliği
  sunucu  -> istemci: selam, bitirme
  istemci -> sunucu : bitirme, ardından uygulama verisi
```

Bu döküm çalıştırılmaz; adımların **ne taşıdığını** gösterir. Dört iş yan yana durur:
takımın seçilmesi, anahtar paylarının değişimi, sertifika zincirinin sunulması ve iki tarafın
kurulumu bitirdiğini birbirine doğrulaması.

Dört iş bağımsız değildir: takım seçilmeden anahtar payları yorumlanamaz, anahtar payları
değişmeden bitirme hesaplanamaz. Adımların sırası bu bağımlılıktan çıkar; turlar
birleştirilebilir ama yer değiştiremez.

Sıranın kendisi bir tasarım kararıdır. Sertifika zinciri, sunucunun **ilk** yanıtıyla gelir;
istemci onu doğrulamak için ayrıca bir istek yapmaz. Önceki dersteki ölçüm bunun karşılığıdır:
zincir doğrulaması sıfır gidiş–dönüşle yürüyordu, çünkü doğrulayacağı her şey zaten elindeydi.
Sürdürme adımlarında sertifika zinciri yoktur — istemci o zinciri bir kez doğrulamış, sonucu
oturumla birlikte saklamıştır.

#### Takımın Seçilmesi ve Bitirme

Dört işten ilki bir pazarlıktır. İstemci desteklediği **şifre takımlarını (cipher suite)**
bildirir, sunucu bunlardan birini seçer. Takım, kurulumdan sonra hangi simetrik dönüşümün ve
hangi özet işlevinin kullanılacağını belirler; ilk dersteki iş bölümü burada somutlaşır.

Pazarlığın bir sorunu vardır ve bu, kursun her dersinde tekrarlanan sorunun aynısıdır: ilk
iki ileti henüz korunmayan bir kanalda gider. Yolun üzerindeki bir katman istemcinin
listesini okuyabilir. Belirtimin buna verdiği yanıt, kurulumun son adımındaki **bitirme**
iletisidir: bitirme, o ana kadar alınıp verilen el sıkışma iletilerinin **tamamının** özeti
üzerinde hesaplanır ve kurulumdan çıkan anahtarla üretilir. İki taraf da karşının bitirmesini
denetler; birinin gördüğü el sıkışma dökümü öbürününkinden farklıysa bitirme tutmaz ve
kurulum tamamlanmaz.

Bu, kursta ilk kez rastlanan bir şeydir. Aracının bütün kararları bir **bildirime**
dayanıyordu ve bildirimi doğrulayan hiçbir şey yoktu. Bitirme, pazarlığın bildirimini
doğrulayan bir adımdır: bildirim sonradan denetlenebilir hâle getirilmiştir. Aradaki fark,
kim tarafından denetlendiğidir — denetimi aracı değil, **uçlar** yapar.

Bitirmenin denetlemediği bir şey kalır: listenin **içeriği**. Bitirme, iki tarafın aynı
listeyi gördüğünü doğrular; listede zayıf bir takımın bulunmasını engellemez. Zayıf takımları
listede tutmak bu yüzden bir **yapılandırma kusurudur**: seçim gerçekten yapılmış, pazarlık
gerçekten doğrulanmış olur ve yine de kurulum kötü bir takıma oturur. *Daraltma:* listenin
kendisi daraltılırsa — yalnız kabul edilebilir takımlar bildirilirse — pazarlığın varabileceği
sonuç kümesi de daralır ve doğrulama işe yarar hâle gelir. Bu bir belirtidir ve burada
yazılan neyin yanlış olduğudur.

#### Bütçe Bağlantı Başına Ödenir

Kurulumun turu alışverişe değil **bağlantıya** yazılır. Kırk alışveriş tek bir bağlantıda
yapılıyorsa kurulum bir kez ödenir; kırk ayrı bağlantıda yapılıyorsa kırk kez.

Aşağıdaki ölçüm tam kurulumu iki gidiş–dönüş, oturum sürdürmeyi bir gidiş–dönüş olarak
**modelliyor**. Bu iki sayı ders içinde seçilmiş model değerleridir; belirtim ailesinin
farklı sürümlerinde farklı çıkarlar ve ders bir sürüm iddiası taşımaz. Ölçülen şey sayıların
kendisi değil, **bağlantı sayısıyla nasıl büyüdükleridir.**

```python
ALISVERIS = 40
TAM_TUR = 2          # ders içinde modellenen tam kurulum bütçesi
SURDURME_TUR = 1     # ders içinde modellenen oturum sürdürme bütçesi

print(f"{'bağlantı':>9s} {'sürdürmesiz kurulum':>20s} {'sürdürmeli kurulum':>19s} "
      f"{'kazanılan tur':>14s} {'toplam tur':>11s} {'alışveriş başına':>17s}")
for baglanti in (40, 8, 4, 1):
    sursuz = baglanti * TAM_TUR
    surdurmeli = TAM_TUR + (baglanti - 1) * SURDURME_TUR
    toplam = surdurmeli + ALISVERIS
    print(f"{baglanti:9d} {sursuz:20d} {surdurmeli:19d} {sursuz - surdurmeli:14d} "
          f"{toplam:11d} {toplam / ALISVERIS:17.3f}")
```

```
 bağlantı  sürdürmesiz kurulum  sürdürmeli kurulum  kazanılan tur  toplam tur  alışveriş başına
       40                   80                  41             39          81             2.025
        8                   16                   9              7          49             1.225
        4                    8                   5              3          45             1.125
        1                    2                   2              0          42             1.050
```

Kırk ayrı bağlantı kurulan rejimde kurulum, sürdürme olmadan **80 tur** tutar; kırk
alışverişin kendisi 40 tur tuttuğuna göre bağlantı kurmak, iş yapmaktan **iki kat** pahalıdır.
**Oturum sürdürme (session resumption)** bu satırda 39 turu geri verir ve toplamı 81'e
indirir; alışveriş başına 2,025 tur düşer.

İki gözlem tabloyu okumaya yeter. Birincisi, sürdürmenin kazandırdığı tur **bağlantı sayısına
bağlıdır**: tek kalıcı bağlantıda kazanç sıfırdır, çünkü sürdürülecek bir şey yoktur.
İkincisi, sürdürme kurulumu ortadan kaldırmaz — yalnız **ucuzlatır**. Kırk bağlantılık
satırda bile alışveriş başına 1,000 turun üzerinde kalınır; sıfıra inen tek yol bağlantıyı
kalıcı tutmaktır.

Sürdürmenin turdan başka kazandırdığı bir şey daha vardır ve bu, önceki derse bağlanır:
sürdürülen oturumda sertifika zinciri yeniden sunulmaz, dolayısıyla zincir doğrulaması da
yeniden yapılmaz. Bir kez alınmış bir karar, oturum yaşadığı sürece yeniden alınmaz. Bunun
bedeli de aynı yerdedir: zincir hakkındaki bilgi **eskir** ve oturum bu eskimeyi görmez.

#### Zarf Kapanınca

Kurulum bittiğinde uygulama iletisi zarfın içine girer. Aracı iletinin geldiğini görür,
uzunluğunu görür, hangi konağa gittiğini görür — ama yöntemi, yolu, kişiye özel imini ve
doğrulayıcıyı **göremez.**

Kurs boyunca sayılan karar tablosu şimdi iki rejimde ölçülüyor. `açık` rejiminde aracı beş
alanın hepsini okur; `zarf` rejiminde yalnız konak adını. Kâhin — kurguyu biz ürettiğimiz
için bilinen gerçek — değişmiyor. Kırk alışveriş ve alışveriş başına beş karar, 200 kararlık
bir kümedir; bu kümede ölçülebilen en küçük fark 1/200 = 0,0050'dir.

Bu dersin iki ölçümünün varsayımları:

- **GT13** — Bütçe tablosunda tam kurulum iki, oturum sürdürme bir gidiş–dönüş olarak
  **ders içinde modellenir**; iki değer de seçilmiş model değerleridir ve bir belirtim sürümü
  iddiası taşımaz.
- **GT14** — Kurulumun turu bağlantıya, alışverişin turu alışverişe yazılır: kırk alışveriş
  kırk tur tutar ve sürdürmeli rejimde ilk bağlantı tam kurulumu, kalanlar sürdürmeyi öder.
- **GT15** — Karar tablosunda kursun kırk alışverişi aynı tohumdan yeniden üretilir;
  alışveriş başına beş karar sayılır ve kâhin rejim değişince değişmez.
- **GT16** — İki rejim karşılaştırılır: `açık` rejiminde aracı yöntemi, yolu, konak adını,
  kişiye özel imi ve doğrulayıcıyı okur; `zarf` rejiminde yalnız konak adını. Konak adı
  kurulumun ilk iletisinde, henüz ortak sır yokken bildirildiği için zarfın dışındadır.
- **GT17** — Aracı yolun üzerindedir ve yalnız zarfın dışında kalana bakar; alamadığı her
  karar bir **tur borcudur** — o alışverişte sunucuya bir tur daha atılır.
- **GT18** — Bayt sütunu aracının okuyabildiği alanların sayımıdır, gerçek iletinin uzunluğu
  değil; ölçüm bir ağ ölçümü değildir.

```python
TOHUM = 20260809
GUVENLI = {"GET", "HEAD"}
ETKISIZ = {"GET", "HEAD", "PUT", "DELETE"}
YONTEMLER = ["GET", "GET", "GET", "HEAD", "POST", "PUT", "DELETE"]
YOLLAR = ["/olcum/kuzey", "/olcum/yamac", "/ozet", "/kayit", "/oturum"]
KARARLAR = ("saklanabilir", "paylasilabilir", "taze", "yinelenebilir", "yonlendirilebilir")
GORUNUR = {"açık": ("yontem", "yol", "konak", "ozel_imi", "dogrulayici"),
           "zarf": ("konak",)}


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def alisverisler(sayi: int = 40) -> list[dict]:
    r, liste = uretec(TOHUM), []
    for i in range(sayi):
        yontem, yol = YONTEMLER[r(7)], YOLLAR[r(5)]
        ozel = yol == "/oturum" or r(5) == 0
        degisti = r(3) == 0
        liste.append({"no": i + 1, "yontem": yontem, "yol": yol,
                      "ozel": ozel, "degisti": degisti,
                      "ozel_imi": ozel and r(4) != 0,
                      "damga_atladi": degisti and r(6) == 0})
    return liste


def kahin(a: dict) -> dict:
    return {"saklanabilir": a["yontem"] in GUVENLI and not a["ozel"],
            "paylasilabilir": not a["ozel"],
            "taze": not a["degisti"],
            "yinelenebilir": a["yontem"] in ETKISIZ,
            "yonlendirilebilir": True}


def ileti(a: dict, gorunur: tuple) -> dict:
    tam = {"yontem": a["yontem"], "yol": a["yol"], "konak": "istasyon.ornek",
           "ozel_imi": a["ozel_imi"],
           "dogrulayici": a["degisti"] and not a["damga_atladi"]}
    return {k: v for k, v in tam.items() if k in gorunur}


def araci(i: dict) -> dict:
    k = {}
    if "yontem" in i:
        k["yinelenebilir"] = i["yontem"] in ETKISIZ
    if "ozel_imi" in i:
        k["paylasilabilir"] = not i["ozel_imi"]
        if "yontem" in i:
            k["saklanabilir"] = i["yontem"] in GUVENLI and not i["ozel_imi"]
    if "dogrulayici" in i:
        k["taze"] = not i["dogrulayici"]
    if "konak" in i:
        k["yonlendirilebilir"] = True
    return k


def bayt(i: dict) -> int:
    return sum(len(k) + len(str(v)) + 4 for k, v in i.items())


def olc(ad: str) -> tuple:
    dogru = yanlis = alinamayan = tur = toplam = 0
    for a in alisverisler():
        gercek, verilen = kahin(a), araci(ileti(a, GORUNUR[ad]))
        eksik = False
        for karar in KARARLAR:
            if karar not in verilen:
                alinamayan += 1
                eksik = True
            elif verilen[karar] == gercek[karar]:
                dogru += 1
            else:
                yanlis += 1
        tur += 1 if eksik else 0
        toplam += bayt(ileti(a, GORUNUR[ad]))
    return dogru, yanlis, alinamayan, tur, toplam


print(f"{'rejim':<8s} {'doğru':>6s} {'yanlış':>7s} {'alınamayan':>11s} "
      f"{'fazladan tur':>13s} {'bayt':>6s}")
for ad in ("açık", "zarf"):
    print(("{:<8s} {:6d} {:7d} {:11d} {:13d} {:6d}").format(ad, *olc(ad)))

print()
print(f"{'karar':<20s} {'açık':>8s} {'zarf':>8s}")
for karar in KARARLAR:
    alinan = [sum(karar in araci(ileti(a, GORUNUR[ad])) for a in alisverisler())
              for ad in ("açık", "zarf")]
    print(f"{karar:<20s} {alinan[0]:8d} {alinan[1]:8d}")
```

```
rejim     doğru  yanlış  alınamayan  fazladan tur   bayt
açık        195       5           0             0   3524
zarf         40       0         160            40    920

karar                    açık     zarf
saklanabilir               40        0
paylasilabilir             40        0
taze                       40        0
yinelenebilir              40        0
yonlendirilebilir          40       40
```

#### Yüz Altmış Kararın Gittiği Yer

Açık rejimde aracı 200 kararın 200'ünü alıyordu ve sunucuya hiç dönmüyordu; 195'i doğru,
5'i yanlıştı. Zarf rejiminde **160 karar alınamıyor.** Bu kararlar yok olmadı; aracı onları
alamadığı için **uçlara taşındılar.** Karar veren yalnız iki uç değildi; şimdi öyle oldu.

İkinci tablo bu 160'ın nereden toplandığını tek tek gösteriyor. Beş karardan dördü —
`saklanabilir`, `paylasilabilir`, `taze`, `yinelenebilir` — açık rejimde kırk alışverişin
kırkında da alınıyor, zarf rejiminde hiçbirinde alınmıyor: dört karar çarpı kırk alışveriş,
160. Ayakta kalan tek karar `yonlendirilebilir`, çünkü tek dayanağı olan konak adı zarfın
dışındadır. Kaybın dağılımı düzgün değil, **keskindir**: bir alanı gizlemek o alana bağlı
kararı azaltmaz, sıfırlar.

Fazladan tur sütunu bunun bedelini gösteriyor: 40 alışverişin **kırkında da** aracının
alamadığı en az bir karar vardır, yani kırkında da sunucuya bir tur daha atılır. Aracının
kaybettiği görünürlük, doğrudan bir tur borcuna dönüşüyor.

Bayt sütunu ise ilk bakışta ters görünür: 3524'ten 920'ye iner. Aracı için ileti küçüldü,
çünkü aracının **okuyabildiği** alanlar küçüldü. Gerçek ileti küçülmedi; yalnız aracının
tarafındaki payı küçüldü. Bu, kursun ikinci iddiasının tersten okunuşudur — orada alan
eklemek karar kazandırmıyordu, burada alan gizlemek bayt kazandırıyor ama karar kaybettiriyor.

Asıl satır ise yanlış sütunudur: **zarf rejiminde 0.** Kalan 40 kararın hiçbiri yanlış
değildir. Bunun nedeni ince değil, doğrudandır: kalan tek karar `yonlendirilebilir` ve o
karar yalnız konak adına bakar; konak adı zarfın dışında kaldığı için de her zaman doğrudur.
Açık rejimdeki 5 yanlış karar, aracının **bildirime inandığı** yerlerden geliyordu — sunucunun
koymayı unuttuğu bir im ve doğrulayıcının kaçırdığı bir değişim. Aracı okuyamadığı bir alana
inanamaz.

Buradan çıkan cümle kursun üçüncü iddiasıdır ve bir kusur tarifi değildir: **az karar, ama
yanlışsız.** Zarf rejimi, aracının 200 karardan 40'ını almasına izin verir; karşılığında
aracının yanılma yüzeyini sıfıra indirir. Hangisinin tercih edileceği bir ölçüm sorusu değil,
bir tasarım kararıdır.

#### Kalan Yüzey ve Daraltmaları

Zarf bir kayıt biçimidir: uygulama iletisi parçalara ayrılır, her parça uzunluğuyla birlikte
sarılır ve öyle taşınır. Bu, aracının gördüğü şeyin sınırını çizer. Aracı kayıtların
sınırlarını ve uzunluklarını görür — çünkü kayıt sınırını görmeden iletiyi ileri
taşıyamaz — ama kaydın içindekini görmez. Karar tablosundaki 160'ın sıfırlanması bu ayrımın
sonucudur: aracının elinde kalan şey iletinin **kabıdır**, içeriği değil.

Zarf her şeyi kapatmaz. Aracının hâlâ gördükleri ve her birinin daraltması:

- **Konak adı.** Kurulumun ilk iletisinde, henüz ortak sır yokken bildirilir; ölçümde
  `yonlendirilebilir` kararının tek dayanağı budur. *Daraltma:* adın da kurulumun korunan
  bölümüne alındığı düzenekler vardır; o durumda tablonun son satırı da alınamayana geçer ve
  aracı yönlendirme kararını da kaybeder.
- **Sertifika zinciri.** Tam kurulumda zincir açıkta sunulur ve konunun adını taşır.
  *Daraltma:* sürdürülen oturumlarda zincir hiç sunulmaz; bağlantı sayısı azaldıkça bu yüzey
  de küçülür.
- **İleti uzunluğu ve zamanlama.** Zarf gövdeyi kapatır, uzunluğunu ve varış anını kapatmaz.
  *Daraltma:* dolgu ve birleştirme, uzunluğu bir bandın içine iter.

Bir aracının 160 kararı geri kazanmasının tek bir yolu vardır ve bu bir açık değil, bir
yerleştirme kararıdır: aracı zarfı **kendisi sonlandırır**. O zaman iki ayrı kurulum olur —
istemciyle aracı arasında bir tane, aracıyla sunucu arasında bir tane — ve aracı ortada açık
rejime döner, tablonun ilk satırındaki 200 kararı yeniden alır. Bedeli tablodan okunmaz ama
tanımdan okunur: uçlar artık birbiriyle değil, ikisi de aracıyla kurulum yapmaktadır ve
istemcinin doğruladığı zincir sunucunun değil aracının zinciridir. *Daraltma:* böyle bir
aracının çıpası istemciye ayrıca yerleştirilmek zorundadır; yerleştirilmediği sürece zincir
doğrulaması düşer ve karar kaybı olduğu yerde kalır.

Bir de sık karşılaşılan **yapılandırma kusuru** vardır: kurulumu yapıp sertifika zincirini
doğrulamadan geçmek. Kazanç yereldir ve önceki dersin ölçümünde sayıldı — hiçbir tur
kazandırmaz. Ödenen şey, zarfın koruduğu bağın **kiminle** kurulduğunun bilinmemesidir. Bu
dersin söylediği neyin yanlış olduğudur; bunun nasıl yapılacağı yazılmaz.

#### Özet

- El sıkışma takım seçimini, anahtar paylarının değişimini, sertifika zincirinin sunulmasını
  ve karşılıklı bitirmeyi tek bir kurulumda toplar; zincir ilk yanıtla geldiği için
  doğrulaması ayrı tur istemez.
- Takım pazarlığı henüz korunmayan bir kanalda yapılır; bitirme iletisi el sıkışmanın
  tamamının özeti üzerinde durduğu için pazarlığı uçlar denetler, aracı değil.
- Kurulumun bütçesi alışverişe değil bağlantıya yazılır: 40 bağlantıda sürdürmesiz 80 tur,
  sürdürmeli 41 tur; tek kalıcı bağlantıda 2 tur.
- Oturum sürdürme 40 bağlantılık rejimde 39 tur kazandırır ve zincir doğrulamasını
  yinelemez; tek bağlantıda kazancı sıfırdır.
- Zarf kapandığında 200 kararın 160'ı aracıdan uçlara taşınır, 40 alışverişin kırkında da bir
  tur borcu doğar ve aracının okuduğu bayt 3524'ten 920'ye iner.
- Kalan 40 kararın **hiçbiri yanlış değildir**; açık rejimdeki 5 yanlış karar aracının
  bildirime inandığı yerlerden geliyordu ve aracı okuyamadığı alana inanamaz.

#### Sonraki Adım

Zincir doğrulaması yerel yürüdü, el sıkışma bütçesini bağlantı başına ödedi ve zarf
kapandığında 160 karar uçlara taşındı. Bu tablonun içinde bir tek şey ölçülmeden kaldı:
istemcinin doğruladığı zincir, doğrulandığı **anda** geçerliydi. Sertifikanın bir başlangıcı
ve bir bitişi vardır, ikisinin arasında iptal edilebilir ve iptal belgenin içine yazılamaz.
Sonraki ders sertifikanın yaşam döngüsünü ve iptalin tur bütçesine ne eklediğini sayacak:
zincir sormadan doğrulanıyordu, iptal sorulmadan bilinemez.

### Sertifika Yaşam Döngüsü

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/sertifika-yasam-dongusu

İptalin bir tur borcu olması: sormamanın kaç alışverişe mal olduğu, sormanın alışveriş başına eklediği tur, arada duran seçeneklerin bilme gecikmesi ve yanlış yapılandırmanın istemcide görülen belirtileri.

Önceki ders taşıma zarfını kapattı ve aracının kaybettiğini saydı. Kapanan zarfın altında bir
kabul vardı: istemci sunucunun zincirini doğrulamış, kurulumu o kabulün üzerine oturtmuştu.
O doğrulama, yapıldığı **anda** geçerliydi.

Sertifikanın bir başlangıcı ve bir bitişi vardır; ikisinin arasında iptal edilebilir. İptal
belgenin içine yazılamaz, çünkü belge imzalandığı anda donar — sonradan yazılan her şey
imzayı düşürür. Bu dersin sorusu buradan çıkar: belgenin içine yazılamayan bir şey nasıl
bilinir ve bilinmesi kaça mal olur? Zincir doğrulaması **sormadan** yürüyordu; iptal
**sorulmadan** bilinemez. Aradaki fark bir tur borcudur.

#### Belgenin Ömrü ve İptalin Yeri

Ömür, sertifikanın geçerlilik alanındaki iki damganın arasıdır ve bu iki damga belgenin
**içindedir**, imzanın altındadır. Bu yüzden geçerlilik denetimi yereldir: istemci kendi
saatine bakar, kimseye sormaz.

**Yenileme (renewal)** ömür bitmeden yeni bir belge çıkarmaktır. Eski belgeyi uzatmaz;
uzatamaz, çünkü uzatmak imzalanmış bir alanı değiştirmek olurdu. Yeni belge üretilir, eski
belge kendi bitişine kadar geçerli kalır ve ikisi bir süre birlikte yaşar. Anahtar değişmişse
eski belge yeni anahtar hakkında hiçbir şey söylemez.

**İptal (revocation)** ömür dolmadan bağın geçersiz sayılmasıdır. Nedenleri belgenin
dışındadır: gizli anahtarın elden çıkması, adın el değiştirmesi, bağın baştan yanlış
kurulmuş olması. Hiçbiri belgeye sonradan yazılamayacağı için veren tarafın ayrı bir kayıt
yayımlaması gerekir.

```text
yaşam döngüsü
  istek -> imzalama -> dağıtım -> kullanım -> yenileme -> ... -> sona erme
                                     |
                                     +-- iptal: her an, belgenin dışından

ömrü taşıyan alanlar
  seri no      : veren içinde belgeyi tekil olarak adlandırır
  geçerlilik   : başlangıç .. bitiş        <- ömür bu aralıktır
  veren        : ara-1.ornek               <- iptali yayımlayacak taraf

iptal kaydı
  liste veren   : ara-1.ornek
  yayım anı     : bu listenin üretildiği an
  sonraki yayım : listenin tazeleneceği an  <- bilme gecikmesinin üst sınırı
  girdiler      : seri no | iptal anı | neden
```

Bu döküm çalıştırılmaz; alanların ne bildirdiğini gösterir. Kayıt belgeyi seri numarasıyla
adlandırır ve belgeyi değiştirmez — ona **dışarıdan** bir not düşer. Asimetri buradadır:
geçerlilik belgenin içinden okunur, iptal belgenin dışından. Birincisi tur istemez, ikincisi
ister.

#### Ölçümün Kurgusu

Kurgu kursun geri kalanıyla aynıdır: Kuzey Yamaç ölçüm istasyonu bir istemciye ölçüm sunar
ve istemci her alışverişte sunulan sertifikanın hâlâ geçerli olup olmadığına karar verir.

- **GT1.** Ölçüm penceresi 960 saattir ve içinde kırk alışveriş yapılır; alışveriş anları
  pencere içinde üreteçten alınır.
- **GT2.** İptal anı pencerenin her saatinde ayrı denenir: 960 an süpürülür, her an için
  kırk alışveriş değerlendirilir.
- **GT3.** Kâhin — kararın gerçeği — kurguyu biz ürettiğimiz için bilinir: iptal anından
  sonraki her alışverişte sertifika geçersizdir.
- **GT4.** İstemcinin iptali öğrendiği ana kadar geçen süre **bilme gecikmesidir**; bu süre
  içine düşen her alışveriş bir **yanlış kabuldür**.
- **GT5.** İptal listesinin tazeleme aralığı 24, sunulan yanıtın tazeliği 6, kısa ömürlü
  sertifikanın ömrü 12, uzun ömürlünün ömrü 2160 saattir; dördü de ders içinde seçilmiş
  model değerleridir ve ders bir sürüm iddiası taşımaz.
- **GT6.** Zincir üç halkadır — yaprak ve iki ara sertifika; güven çıpası sorulmaz.
- **GT7.** Tur yalnız **istemcinin** ödediği gidiş–dönüştür; sunucunun ya da kayıt
  yayımlayan tarafın kendi turu bu sütuna girmez.
- **GT8.** Hiçbir bağlantı açılmaz, hiçbir sertifika çözümlenmez; dökümler kurgudur.

Tek bir iptal anında küme kırk karardır, çözünürlüğü 1/40 = 0,025'tir; ortalama sütunları
960 anın tamamından, yani 38.400 karardan alınmıştır.

#### Sormamanın ve Sormanın Maliyeti

```python
TOHUM = 20260809
PENCERE = 960            # saat: kırk günlük ölçüm penceresi
OMUR = 2160              # saat: uzun ömürlü sertifikanın modellenen ömrü
HALKA = 3                # yaprak + iki ara sertifika; çıpa sorulmaz


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


uret = uretec(TOHUM)
ZAMANLAR = sorted(uret(959) for _ in range(40))

REJIMLER = (
    # ad, alışveriş başına tur, tazeleme aralığı (saat), istemci kendi çeker mi
    ("sormamak", 0, None, False),
    ("her alışverişte sormak", 1, 0, True),
    ("önceden dağıtılmış iptal listesi", 0, 24, True),
    ("sertifikayla sunulan yanıt", 0, 6, False),
    ("kısa ömürlü sertifika", 0, 12, False),
)


def bilme_ani(iptal: int, aralik) -> int:
    if aralik is None:
        return OMUR
    if aralik == 0:
        return iptal
    return iptal + aralik - iptal % aralik


def olc(aralik) -> tuple[float, int, float, int]:
    gecikme_top = yanlis_top = gecikme_kotu = yanlis_kotu = 0
    for iptal in range(PENCERE):
        bilme = bilme_ani(iptal, aralik)
        yanlis = sum(1 for t in ZAMANLAR if iptal <= t < bilme)
        gecikme_top, yanlis_top = gecikme_top + bilme - iptal, yanlis_top + yanlis
        gecikme_kotu = max(gecikme_kotu, bilme - iptal)
        yanlis_kotu = max(yanlis_kotu, yanlis)
    return gecikme_top / PENCERE, gecikme_kotu, yanlis_top / PENCERE, yanlis_kotu


def istemci_turu(basina: int, aralik, ceker: bool, sayi: int, halka: int) -> int:
    tur = basina * sayi * halka
    return tur + (PENCERE // aralik * halka if ceker and aralik else 0)


print(f"{'rejim':<33s}{'tur/alışveriş':>14s}{'istemci turu':>13s}"
      f"{'bilme gecikmesi':>18s}{'yanlış kabul':>14s}")
for ad, basina, aralik, ceker in REJIMLER:
    og, kg, oy, ky = olc(aralik)
    print(f"{ad:<33s}{basina:14d}"
          f"{istemci_turu(basina, aralik, ceker, len(ZAMANLAR), HALKA):13d}"
          f"{f'{og:.1f} / {kg}':>18s}{f'{oy:.2f} / {ky}':>14s}")

print()
print(f"{'halka':>6s}{'her alışverişte sorma':>23s}{'iptal listesi':>15s}"
      f"{'sunulan yanıt':>15s}")
for h in range(1, 6):
    satir = [istemci_turu(b, a, c, len(ZAMANLAR), h)
             for _, b, a, c in REJIMLER[1:4]]
    print(f"{h:6d}{satir[0]:23d}{satir[1]:15d}{satir[2]:15d}")

print()
print(f"{'alışveriş':>10s}{'her alışverişte sorma':>23s}{'iptal listesi':>15s}")
for n in (40, 120, 400, 4000):
    satir = [istemci_turu(b, a, c, n, HALKA) for _, b, a, c in REJIMLER[1:3]]
    print(f"{n:10d}{satir[0]:23d}{satir[1]:15d}")

print()
print(f"alışveriş {len(ZAMANLAR)}, iptal anı {PENCERE}, "
      f"karar {len(ZAMANLAR) * PENCERE}")
```

```
rejim                             tur/alışveriş istemci turu   bilme gecikmesi  yanlış kabul
sormamak                                      0            0     1680.5 / 2160    22.02 / 40
her alışverişte sormak                        1          120           0.0 / 0      0.00 / 0
önceden dağıtılmış iptal listesi              0          120         12.5 / 24      0.57 / 4
sertifikayla sunulan yanıt                    0            0           3.5 / 6      0.15 / 2
kısa ömürlü sertifika                         0            0          6.5 / 12      0.31 / 3

 halka  her alışverişte sorma  iptal listesi  sunulan yanıt
     1                     40             40              0
     2                     80             80              0
     3                    120            120              0
     4                    160            160              0
     5                    200            200              0

 alışveriş  her alışverişte sorma  iptal listesi
        40                    120            120
       120                    360            120
       400                   1200            120
      4000                  12000            120

alışveriş 40, iptal anı 960, karar 38400
```

İlk tablonun ilk iki satırı dersin iki ucudur. Hiç sormayan istemci **sıfır tur** harcar ve
iptal edilmiş bir sertifikayı ortalama **22,02 alışveriş** boyunca geçerli sayar; en kötü
durumda kırkında da. Nedeni ince değildir: sormayan istemcinin tek bilgi kaynağı belgenin
kendi bitişidir, o bitiş de pencereden uzaktadır.

Her alışverişte soran istemci yanlış kabulü **sıfıra** indirir. Ödediği 120 tur iki çarpandan
gelir: kırk alışveriş çarpı üç halka. Alışveriş başına tur 1 görünür, ama gerçekte **halka
sayısı kadar** soru sorulur, çünkü zincirin her halkası ayrı ayrı iptal edilebilir.

İkinci tablo bu çarpanı açar ve kursun eksenindeki ayrımı keskinleştirir. Sertifikalar ve
Güven Zinciri dersinde zincir doğrulamasının turu zincir uzunluğundan **bağımsızdı**; iptal
sorusunun turu ona **doğru orantılıdır**: bir halkada 40, beş halkada 200. Belgenin içi
zincirle birlikte gelir; dışı gelmez ve her halka için ayrıca sorulur.

Üçüncü tablo bölenin ne olduğunu gösteriyor. Sorma turu alışveriş sayısıyla doğrusal büyür —
kırk alışverişte 120, dört binde 12.000 — ama önceden dağıtılmış listenin turu **sabit
kalır**: 120. Liste pencere başına ödenir, soru alışveriş başına. Kırk alışverişlik satırda
ikisinin eşit çıkması rastlantıdır; ayrım toplamda değil, **turun nereye yazıldığındadır**.

#### Aradaki Seçenekler

Tablonun son üç satırı iki ucun arasını doldurur ve üçünün ortak özelliği şudur: **alışveriş
başına tur sıfırdır.** Hiçbiri alışverişin yoluna gidiş–dönüş eklemez; ayrıştıkları yer bilme
gecikmesidir.

**Önceden dağıtılmış iptal listesi** günde bir tazelenir; gecikme ortalama 12,5 saat, en kötü
24 saattir ve ortalama 0,57 alışveriş yanlış kabul edilir. **Sertifikayla sunulan yanıt**,
verenden alınmış geçerlilik yanıtının kurulumda sertifikayla birlikte gönderilmesidir;
istemcinin turu sıfırdır, çünkü yanıt zaten gelen bir iletinin içindedir. Gecikme 3,5 saate,
yanlış kabul 0,15'e iner. **Kısa ömürlü sertifika** ise iptal düzeneğini bütünüyle atar:
belge o kadar kısa yaşar ki iptal edilmesine gerek kalmaz, gecikme kalan ömürdür — ortalama
6,5 saat, yanlış kabul 0,31.

Üç satır yan yana okunduğunda bir düzen çıkıyor. Gecikme 3,5 iken yanlış kabul 0,15; gecikme
6,5 iken 0,31; gecikme 12,5 iken 0,57. Yanlış kabul, gecikmeyle birlikte ve kabaca onunla
orantılı büyüyor. Yani düzeneklerin adları farklı olsa da ölçülen şey tek bir sayıdır:
**mekanizma seçimi bir gecikme seçimidir.**

İptal sorusunun kendisi de sayılması gereken bir yüzeydir: soru, hangi belgeye bakıldığını
yanıtlayan tarafa bildirir ve her alışverişte sorulursa bildirim alışveriş sıklığını da
taşır. *Daraltma:* yanıt sertifikayla birlikte sunulduğunda istemci soruyu hiç sormaz,
dolayısıyla bakılan belgeyi bildiren de olmaz; önceden dağıtılmış liste ise tek tek belgeleri
değil listenin tamamını taşıdığı için hangi belgeye bakıldığını bildirmez. Sormamanın yüzeyi tablonun ilk satırındadır — ortalama
22,02 alışveriş. *Daraltma:* bu pencereyi daraltan tek şey belgenin ömrüdür.

#### Ömür Kısaldıkça

```python
TOHUM = 20260809
PENCERE = 960            # saat: kırk günlük ölçüm penceresi
OMURLER = (2160, 720, 168, 72, 24, 12, 6)   # saat


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


uret = uretec(TOHUM)
ZAMANLAR = sorted(uret(959) for _ in range(40))


def olc(omur: int) -> tuple[float, float, int]:
    gecikme_top = yanlis_top = yanlis_kotu = 0
    for iptal in range(PENCERE):
        bilme = iptal + omur - iptal % omur
        yanlis = sum(1 for t in ZAMANLAR if iptal <= t < bilme)
        gecikme_top, yanlis_top = gecikme_top + bilme - iptal, yanlis_top + yanlis
        yanlis_kotu = max(yanlis_kotu, yanlis)
    return gecikme_top / PENCERE, yanlis_top / PENCERE, yanlis_kotu


print(f"{'ömür (saat)':>12s}{'yenileme':>10s}{'ort gecikme':>13s}"
      f"{'yanlış kabul':>14s}{'yenileme x ömür':>17s}")
for omur in OMURLER:
    og, oy, ky = olc(omur)
    yenileme = PENCERE // omur
    print(f"{omur:12d}{yenileme:10d}{og:13.1f}{f'{oy:.2f} / {ky}':>14s}"
          f"{yenileme * omur:17d}")
```

```
 ömür (saat)  yenileme  ort gecikme  yanlış kabul  yenileme x ömür
        2160         0       1680.5    22.02 / 40                0
         720         1        420.5    13.02 / 28              720
         168         5         87.5      3.82 / 8              840
          72        13         37.1      1.32 / 4              936
          24        40         12.5      0.57 / 4              960
          12        80          6.5      0.31 / 3              960
           6       160          3.5      0.15 / 2              960
```

Bu tabloda iptal düzeneği yoktur; istemci bir şeyin geçersizleştiğini yalnız belge sona
erdiğinde öğrenir. İki sütun ters yönde yürüyor. Ömür 2160 saatken pencere içinde hiç
yenileme yapılmaz, gecikme ortalama 1680,5 saattir ve 22,02 alışveriş yanlış kabul edilir.
Ömür 6 saate indiğinde gecikme 3,5 saate, yanlış kabul 0,15'e düşer — ama pencere içinde
160 yenileme yapılması gerekir.

Son sütun toplam işin korunduğunu gösteriyor: ömür pencereden kısa olduğu her satırda
yenileme sayısı çarpı ömür pencereye eşitlenir. Kazanılan gecikme, ödenen yenileme sayısıyla
ters orantılıdır. Kırk günde 160 yenileme, ortalama altı saatte bir yenileme demektir; bu
elle yapılabilecek bir iş değildir. **Kısa ömür bir otomasyon koşuludur** ve koşul
sağlanmazsa kazanç maliyete dönüşür.

Bir gözlem daha var ve rastlantı değildir. Bu tablonun 24, 12 ve 6 saatlik satırları,
önceki tablonun iptal listesi, kısa ömürlü sertifika ve sunulan yanıt satırlarıyla **aynı**
sayıları veriyor. Üç ayrı düzenek, tek bir parametrenin işlevidir. Düzeneğin adı yanlış kabul
sayısını değiştirmiyor; değiştirdiği tek şey **turu kimin, ne zaman ve kaç kez ödediğidir.**

#### Yanlış Yapılandırmanın Belirtileri

Doğrulama tek bir karar değil, bir yürüyüştür ve yürüyüşün her adımı ayrı ayrı düşebilir.
Belirti, hangi adımın düştüğünü ele verir.

```text
doğrulama yürüyüşü ve düşebileceği adımlar
  1  sunulan zincir toplandı              <- eksik ara sertifika burada düşer
  2  her halkanın imzası doğrulandı
  3  zincir güven çıpasına vardı mı       <- kendinden imzalı zincir burada düşer
  4  geçerlilik aralığı açık mı           <- süresi geçmiş sertifika burada düşer
  5  yaprağın adı istenen adı karşılıyor mu   <- ad uyuşmazlığı burada düşer
  6  halkalardan biri iptal edilmiş mi    <- yalnız bu adım belgenin dışını sorar
```

- **Süresi geçmiş sertifika.** Belirti: doğrulama dördüncü adımda düşer ve hata **bir anda**
  başlar, çünkü aralık herkes için aynıdır. Ayırt edici im, hatanın ağa, yola ve istemciye
  göre değişmemesidir. *Daraltma:* yenileme bitişten önce tetiklenirse aralık hiç kapanmaz;
  ömür kısaldıkça bu tetikleme otomatikleşmek zorunda kalır.
- **Eksik ara sertifika.** Belirti: aynı sunucu kimi istemcide çalışır, kiminde çalışmaz.
  Eksik halkayı başka bir alışverişte edinip saklamış istemci zinciri tamamlayabilir,
  edinmemiş olan tamamlayamaz. Ayırt edici im, sonucun **istemciye göre değişmesidir.**
  *Daraltma:* eksik halka sunucunun sunduğu zincire eklenerek kapatılır, istemcinin çıpa
  kümesi genişletilerek değil.
- **Ad uyuşmazlığı.** Belirti: imzalar tutar, zincir çıpaya varır, geçerlilik aralığı açıktır
  ve doğrulama yine de beşinci adımda düşer. Ayırt edici im, hatanın **hangi adla**
  gidildiğine bağlı olmasıdır. *Daraltma:* sertifikanın bildirdiği ad kümesi ile sunucunun
  yanıt verdiği ad kümesi tek bir kaynaktan üretilirse ikisi ayrışamaz.
- **Kendinden imzalı zincir.** Belirti: zincir tek halkadır ve o halkanın vereni kendisidir;
  istemcinin çıpa kümesinde karşılığı yoksa üçüncü adımda düşer. Ayırt edici im, hatanın
  çıpayı elle yerleştirmiş istemcide **görülmemesidir.** Bu, kendinden imzalı belgenin
  kusurlu olduğu anlamına gelmez — güven çıpalarının kendisi de çoğunlukla kendinden
  imzalıdır; ayrım, belgenin istemcinin elinde olup olmadığındadır. *Daraltma:* çıpa
  yerleştirilecekse kapsamı tek uca daraltılır, çünkü her çıpa o çıpadan inen bütün
  zincirleri kabul edilebilir kılar.

Dördü de doğrulamanın **bir adımının** düşmesidir. Doğrulamayı bütünüyle kapatmak dört
belirtiyi birden susturur; sustuğu için de bir düzeltme değildir — Sertifikalar ve Güven
Zinciri dersinde doğrulamayı hiç yapmayan istemcinin doğru karar sayısının düştüğü
ölçülmüştü. Bu ders neyin yanlış olduğunu yazar, nasıl yapılacağını değil.

#### Özet

- Ömür belgenin içindedir ve yerel denetlenir; iptal belgenin dışındadır ve imzalanmış alana
  yazılamayacağı için ayrı bir kayıtla yayımlanır.
- Yenileme eski belgeyi uzatmaz, yenisini çıkarır; ikisi bir süre birlikte yaşar ve anahtar
  değişmişse eski belge yeni anahtar hakkında hiçbir şey söylemez.
- Hiç sormayan istemci sıfır tur harcar ve iptal edilmiş sertifikayı ortalama 22,02 alışveriş
  boyunca geçerli sayar; her alışverişte soran istemci yanlış kabulü sıfırlar ve 120 tur öder.
- İptal sorusunun turu zincir uzunluğuyla doğru orantılıdır — bir halkada 40, beş halkada
  200 — ve alışveriş sayısıyla doğrusal büyürken önceden dağıtılmış listenin turu sabit kalır.
- Aradaki üç seçeneğin alışveriş başına turu sıfırdır ve yanlış kabulleri yalnız bilme
  gecikmesine bağlıdır: 3,5 saatte 0,15; 6,5 saatte 0,31; 12,5 saatte 0,57.
- Ömür kısaldıkça gecikme düşer ama yenileme artar; yenileme çarpı ömür pencereye
  sabitlenir, yani kazanılan gecikme ödenen yenilemeyle ters orantılıdır.

#### Sonraki Adım

Güvenli taşıma burada kapanıyor. Anahtar rolleri bölündü, zincir yerel doğrulandı, kurulumun
turu sayıldı ve iptalin tur borcu ödendi. TLS El Sıkışması dersinde ölçülen kayıp da yerinde
duruyor: zarf kapandığında yüz altmış karar aracıdan uçlara taşınmıştı. Ama o kaybın
ortasında bağlantı hâlâ **istek–yanıt** biçimindeydi: istemci bir ileti gönderiyor, sunucu
bir ileti döndürüyor, sıra yeniden istemciye geçiyordu. Aracı iletinin içini göremese de kaç
ileti olduğunu, her birinin nerede başlayıp nerede bittiğini biliyordu. Sonraki konu bunu
soruyor: bağlantının **biçimi** de değişirse geriye ne kalır?

## Diğer Uygulama Protokolleri

Çift yönlü kalıcı bağlantı, tek yönlü sunucu akışı, yoklamanın maliyeti, dosya aktarımının kanal ayrımı, postanın gönderim ve erişim ayrımı, saat eşitleme ve aygıt yönetimi.

### WebSocket

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/websocket

Yükseltme el sıkışmasının bir rejim değişimi olması: 101'den sonra aracının tünele düşmesi, iki yüz kararın tamamının uçlara taşınması, çerçevenin taşıdığı alanların hiçbirinden karar çıkmaması ve gizlemenin bayt cinsinden bedeli.

Önceki ders taşıma zarfını kapattı ve aracının kaybını saydı: iki yüz kararın yüz altmışı
ortadan uçlara taşındı, geriye yalnız konak adından çıkarılan kırk karar kaldı. Kaybın
büyüklüğüne karşın bir şey yerinde durmuştu. Bağlantı hâlâ **istek–yanıt** biçimindeydi:
istemci bir ileti gönderiyor, sunucu bir ileti döndürüyor, sıra yeniden istemciye geçiyordu.
Aracı iletinin içini göremese de kaç ileti olduğunu, her birinin nerede başlayıp nerede
bittiğini biliyordu.

Bu dersin sorusu şudur: bağlantının **biçimi** de değişirse ne kalır? **WebSocket** tam
bunu yapar. Olağan bir HTTP isteğiyle başlar, tek bir yanıtla kendi çerçeve biçimine geçer
ve o noktadan sonra bağlantı üzerinde ne istek satırı, ne başlık alanı, ne de durum kodu
vardır. Aynı bağlantı üzerinde iki uç da birbirine istediği anda yazabilir.

#### Yükseltme El Sıkışması

Bağlantı, aracının okuyabildiği tek bir alışverişle açılır. İstemci olağan bir GET isteği
gönderir; isteği ayıran şey, bağlantının **yükseltileceğini** bildiren başlık alanlarıdır.
Buna **yükseltme el sıkışması (upgrade handshake)** denir.

```text
GET /akis HTTP/1.1
Host: istasyon.ornek
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: <on altı baytlık rastgele değerin base64 kodu>
Sec-WebSocket-Version: 13

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: <anahtar ile belirtimdeki sabitin özetinin base64 kodu>
```

`Sec-WebSocket-Accept` değeri, istemcinin gönderdiği anahtardan belirtimde sabitlenmiş bir
dizgeyle birlikte türetilir. Bu türetmenin **hiçbir gizlilik işlevi yoktur**; yalnız yanıtı
gerçekten yükseltmeyi anlamış bir uçtan geldiğine bağlar: belirtimdeki sabit dizgeyi bilmeyen
bir uç — sıradan bir istek gönderen bir betik gibi — doğru kabul değerini üretemez. Aracı
önbelleğinin zehirlenmesine karşı belirtimin koyduğu önlem bu alan değil, istemciden sunucuya
giden her çerçevenin maskelenmesidir. Kabul değerinin hesabı ve sunucu tarafındaki
kurulumu **Önbellekleme, Kuyruklar ve Eşzamansız İşleme** kursunda yazıldı; orada ölçülen
şey sunucunun her bağlantı için tuttuğu durumdu. **Burada ölçülen, aradaki aracının
iletiden alabildiği karardır.**

Belirleyici satır `101 Switching Protocols`'tür. 100 sınıfı bir ara yanıttır: istek
sürüyor, sonuç henüz yok. 101 ise sürecin sonucunu değil, **artık başka bir protokol
konuşulacağını** bildirir. Bu satırdan sonra bağlantı üzerinde HTTP yoktur.

#### Aracının Ölçüsü

Kursun kurgusu bu derste de aynıdır. Kuzey Yamaç ölçüm istasyonu bir istemciye ölçüm sunar;
arada uygulamayı bilmeyen bir aracı durur ve yalnız iletide yazana bakarak karar verir.

- **DG1.** Kırk alışveriş ve alışveriş başına beş karar ölçülür: `saklanabilir`,
  `paylasilabilir`, `taze`, `yinelenebilir`, `yonlendirilebilir`. Küme iki yüz karardır.
- **DG2.** Kâhin — kararın gerçeği — kurguyu biz ürettiğimiz için bilinir. Aracının verdiği
  yanıt bu gerçekle karşılaştırılır; sormak zorunda kaldığı her alışveriş bir **tur
  borcudur**.
- **DG3.** `açık` rejimde aracı yöntem, yol, konak adı, kişiye özel imi ve doğrulayıcıyı
  okur. `tünel` rejimde hiçbirini okumaz.
- **DG4.** Bir WebSocket bağlantısının üzerinde kırk alışverişin tamamı taşınır; bağlantı
  bir kez açılır ve kapanmaz.
- **DG5.** Dökümler ve çerçeve boyları kurgudur; ders içinde hiçbir bağlantı açılmaz.

```python
TOHUM = 20260809
GUVENLI = {"GET", "HEAD"}
ETKISIZ = {"GET", "HEAD", "PUT", "DELETE"}
YONTEMLER = ["GET", "GET", "GET", "HEAD", "POST", "PUT", "DELETE"]
YOLLAR = ["/olcum/kuzey", "/olcum/yamac", "/ozet", "/kayit", "/oturum"]
KARARLAR = ("saklanabilir", "paylasilabilir", "taze", "yinelenebilir",
            "yonlendirilebilir")
GORUNUR = {"açık": ("yontem", "yol", "konak", "ozel_imi", "dogrulayici"),
           "tünel": ()}


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def alisverisler(sayi: int = 40) -> list[dict]:
    r, liste = uretec(TOHUM), []
    for i in range(sayi):
        yontem, yol = YONTEMLER[r(7)], YOLLAR[r(5)]
        ozel = yol == "/oturum" or r(5) == 0
        degisti = r(3) == 0
        liste.append({"no": i + 1, "yontem": yontem, "yol": yol, "ozel": ozel,
                      "degisti": degisti, "ozel_imi": ozel and r(4) != 0,
                      "damga_atladi": degisti and r(6) == 0})
    return liste


def kahin(a: dict) -> dict:
    return {"saklanabilir": a["yontem"] in GUVENLI and not a["ozel"],
            "paylasilabilir": not a["ozel"], "taze": not a["degisti"],
            "yinelenebilir": a["yontem"] in ETKISIZ, "yonlendirilebilir": True}


def ileti(a: dict, gorunur: tuple) -> dict:
    tam = {"yontem": a["yontem"], "yol": a["yol"], "konak": "istasyon.ornek",
           "ozel_imi": a["ozel_imi"],
           "dogrulayici": a["degisti"] and not a["damga_atladi"]}
    return {k: v for k, v in tam.items() if k in gorunur}


def araci(i: dict) -> dict:
    k = {}
    if "yontem" in i:
        k["yinelenebilir"] = i["yontem"] in ETKISIZ
    if "ozel_imi" in i:
        k["paylasilabilir"] = not i["ozel_imi"]
        if "yontem" in i:
            k["saklanabilir"] = i["yontem"] in GUVENLI and not i["ozel_imi"]
    if "dogrulayici" in i:
        k["taze"] = not i["dogrulayici"]
    if "konak" in i:
        k["yonlendirilebilir"] = True
    return k


def olc(ad: str) -> tuple:
    dogru = yanlis = alinamayan = tur = bayt = 0
    for a in alisverisler():
        gercek, verilen = kahin(a), araci(ileti(a, GORUNUR[ad]))
        eksik = False
        for karar in KARARLAR:
            if karar not in verilen:
                alinamayan, eksik = alinamayan + 1, True
            elif verilen[karar] == gercek[karar]:
                dogru += 1
            else:
                yanlis += 1
        tur += 1 if eksik else 0
        bayt += sum(len(k) + len(str(v)) + 4
                    for k, v in ileti(a, GORUNUR[ad]).items())
    return dogru, yanlis, alinamayan, tur, bayt


print(f"{'rejim':<7s} {'doğru':>6s} {'yanlış':>7s} {'alınamayan':>11s} "
      f"{'tur borcu':>10s} {'bayt':>6s}")
for ad in ("açık", "tünel"):
    d, y, e, t, b = olc(ad)
    print(f"{ad:<7s} {d:6d} {y:7d} {e:11d} {t:10d} {b:6d}")
```

```
rejim    doğru  yanlış  alınamayan  tur borcu   bayt
açık       195       5           0          0   3524
tünel        0       0         200         40      0
```

Yükseltme öncesinde aracı kırk alışverişin tamamını okuyordu ve iki yüz kararın iki yüzünü
kendi başına alıyordu; sunucuya fazladan hiçbir tur atmıyordu. Yükseltmeden sonra elinde
kalan karar sayısı **sıfırdır** ve kırk alışverişin kırkı birer tur borcuna dönüşür.

İki satır arasındaki fark, taşıma zarfının farkından da büyüktür. Zarf rejiminde aracı
konak adını görüyor ve `yonlendirilebilir` kararını kırk kez alabiliyordu; tünelde onu da
kaybeder. Bunun tek nedeni şifreleme değildir: **101'den sonra bağlantı üzerinde
`yonlendirilebilir` kararını besleyecek bir alan yeniden görünmez.** Yönlendirme kararı
bağlantının kurulduğu anda bir kez alınmış, sonrasında dondurulmuştur.

#### El Sıkışma, Aracının Okuduğu Son İletidir

Yükseltme el sıkışması bir istisna değil, bir **sınırdır**. Bağlantı üzerinde taşınan
iletileri tek tek aynı karar işlevinden geçirmek bunu gösterir.

```python
GUVENLI = {"GET", "HEAD"}
ETKISIZ = {"GET", "HEAD", "PUT", "DELETE"}


def araci(i: dict) -> dict:              # önceki bloktaki karar işlevinin aynısı
    k = {}
    if "yontem" in i:
        k["yinelenebilir"] = i["yontem"] in ETKISIZ
    if "ozel_imi" in i:
        k["paylasilabilir"] = not i["ozel_imi"]
        if "yontem" in i:
            k["saklanabilir"] = i["yontem"] in GUVENLI and not i["ozel_imi"]
    if "dogrulayici" in i:
        k["taze"] = not i["dogrulayici"]
    if "konak" in i:
        k["yonlendirilebilir"] = True
    return k


BAGLANTI = [
    ("yükseltme isteği", {"yontem": "GET", "yol": "/akis",
                          "konak": "istasyon.ornek", "yukseltme": "websocket"}),
    ("101 yanıtı", {"yukseltme": "websocket", "kabul": "kurgu-deger"}),
    ("metin çerçevesi", {"fin": 1, "opkod": 1, "maskeli": True, "uzunluk": 88}),
    ("kapat çerçevesi", {"fin": 1, "opkod": 8, "maskeli": True, "uzunluk": 2}),
]

print(f"{'ileti':<18s} {'alan':>5s} {'karar':>6s}  alınan kararlar")
for ad, m in BAGLANTI:
    k = araci(m)
    print(f"{ad:<18s} {len(m):5d} {len(k):6d}  {', '.join(k) or '-'}")
```

```
ileti               alan  karar  alınan kararlar
yükseltme isteği       4      2  yinelenebilir, yonlendirilebilir
101 yanıtı             2      0  -
metin çerçevesi        4      0  -
kapat çerçevesi        4      0  -
```

Yükseltme isteği beş kararın **ikisini** verir: yöntem alanından `yinelenebilir`, konak
alanından `yonlendirilebilir`. Kalan üçü yanıtın alanlarını gerektirir ve o yanıt hiç
gelmez — gelen yanıt 101'dir ve kişiye özel imi de doğrulayıcı da taşımaz. Bu iki karar,
bağlantının ömrü boyunca aracının elindeki tek karardır.

#### Çerçeve Görünse de Karar Çıkmazdı

Yukarıdaki tabloda çerçeveler **dört alan** taşıyor ve yine de sıfır karar üretiyor. Bu,
kaybın tek başına gizlemeden gelmediğini söyler. Çerçevenin yerleşimi belirtimde
sabitlenmiştir ve taşıdığı alanların hepsi **taşıma hakkındadır**, ileti hakkında değil.

```text
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| işlem |M|   uzunluk   |  genişletilmiş uzunluk (16 bit)|
|I|S|S|S| kodu  |A|    (7 bit)  |  uzunluk alanı 126 ise         |
|N|V|V|V| (4bit)|S|             |                               |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+-------------------------------+
|      maske anahtarı (32 bit, maske biti 1 ise)                |
+---------------------------------------------------------------+
|                        yük verisi                             |
+---------------------------------------------------------------+
```

**Çerçeveleme (framing)**, bayt akışını ileti sınırlarına bölmenin kuralıdır. Uzunluk alanı
yedi bittir; 125'e kadar olan yükler doğrudan yazılır, 126 değeri iki baytlık, 127 değeri
sekiz baytlık bir uzunluk alanının izlediğini bildirir. Maske biti kurulduğunda dört
baytlık bir anahtar eklenir ve yük bu anahtarla katlanır. Maske istemci yönünde zorunludur.

```python
ISLEM = {0: "sürdürme", 1: "metin", 2: "ikili", 8: "kapat", 9: "yokla",
         10: "karşılık"}


def basluk_boyu(yuk: int, maskeli: bool) -> int:
    n = 2                                  # FIN, RSV, işlem kodu, maske biti, uzunluk
    n += 8 if yuk > 65535 else 2 if yuk > 125 else 0   # genişletilmiş uzunluk
    n += 4 if maskeli else 0               # maske anahtarı yalnız istemci yönünde
    return n


print("işlem kodları:", ", ".join(f"{k}={v}" for k, v in ISLEM.items()))
print()
print(f"{'yük':>6s} {'yön':<15s} {'başlık':>7s} {'toplam':>7s} {'başlık payı':>12s}")
for yuk, maskeli in ((2, True), (88, True), (125, True), (126, True),
                     (88, False), (70000, False)):
    b = basluk_boyu(yuk, maskeli)
    yon = "istemci→sunucu" if maskeli else "sunucu→istemci"
    print(f"{yuk:6d} {yon:<15s} {b:7d} {b + yuk:7d} {b / (b + yuk):12.3%}")
```

```
işlem kodları: 0=sürdürme, 1=metin, 2=ikili, 8=kapat, 9=yokla, 10=karşılık

   yük yön              başlık  toplam  başlık payı
     2 istemci→sunucu        6       8      75.000%
    88 istemci→sunucu        6      94       6.383%
   125 istemci→sunucu        6     131       4.580%
   126 istemci→sunucu        8     134       5.970%
    88 sunucu→istemci        2      90       2.222%
 70000 sunucu→istemci       10   70010       0.014%
```

Açık rejimde aracının okuduğu 3524 bayt kırk alışverişe bölündüğünde alışveriş başına 88
bayt düşer; çerçeve başlığı aynı yükü sunucu yönünde **iki bayta** sarar. Kazanç gerçektir
ve küçük iletilerde belirgindir. Ama karşılığı da tam olarak ölçülmüştür: 88 baytın iki
yüz kararı, iki baytın sıfır kararıdır.

Bu, kursun ikinci iddiasının tersten okunuşudur. Orada alan eklemenin karar
kazandırmadığını görmüştük: üç alan daha, bayt artışı, sıfır yeni karar. Burada aynı şey
öbür yönde çıkıyor — **baytı kısmak da karar kaybettirmez; kararı kaybettiren, alanın
kendisinin ortadan kalkmasıdır.** Bayt ile karar arasında bir bağ yoktur; bağ, alan ile
karar arasındadır.

#### Aracının Elinde Kalan Tek Kol

Aracı karar alamaz ama bağlantıyı taşımaya devam eder ve taşıdığı şey üzerinde tek bir
yetkisi kalır: **onu kapatmak.** Boşta duran bir bağlantıyı belirli bir süre sonra kesmek —
**boşta bekleme süresi (idle timeout)** — bir aracının açık tuttuğu bağlantı sayısını
sınırlamak için başvurduğu olağan kuraldır. Açık
rejimde bu kural zararsızdır; istek–yanıt biçiminde bağlantı zaten iletiler arasında
boşalır ve yeniden kurulabilir. Tünelde ise bağlantının kendisi durumun taşıyıcısıdır:
kesilen bağlantı, iki ucun üzerinde anlaştığı ne varsa onu da götürür.

Belirtim buna iki **denetim çerçevesi** ile karşılık verir. **Yokla (ping)** ve **karşılık
(pong)** işlem kodları uygulama verisi taşıyabilir — karşılık, yoklamanın taşıdığı baytı
aynen geri verir — ama bu ders için işlevleri bağlantı üzerinde bayt akıtmak ve iki ucun
ayakta olduğunu doğrulamaktır. Bunlar aracıya bir şey **anlatmaz** — tabloda ölçüldüğü
gibi karar üretmezler — yalnız aracının boşta sayacını sıfırlar. Aracının iletiden
alabildiği karar sıfırken bile bağlantı üzerinde bir tasarım kısıtı doğurması, bu dersin
sessiz sonucudur.

Bir alan daha yükseltme isteğinde taşınabilir: `Sec-WebSocket-Protocol` başlığı, çerçevelerin
hangi kurala göre yorumlanacağını adlandırır ve sunucu seçtiği adı yanıtta yineler. Bu ad,
aracının bağlantı üzerinde göreceği **anlamla ilgili tek işarettir** ve o da yalnız açılış
alışverişinde bir kez geçer. Sonrasında aracı, adı bildiği ama içeriğini okuyamadığı bir
akışı iletir.

#### Kararların Taşındığı Yer

İki yüz karar yok olmaz, taşınır. Tünel rejiminde her biri artık bir uçta alınmak
zorundadır ve bu, uygulamaya yazılacak iş demektir.

| Karar | Açık rejimde kimin | Tünelde kimin | Uçta ne gerektirir |
|---|---|---|---|
| `yinelenebilir` | aracı | gönderen uç | ileti kimliği ve yineleme kuralı |
| `saklanabilir` | aracı | alan uç | uygulama içi kopya ve geçersizleştirme |
| `paylasilabilir` | aracı | sunucu | kapsamı çerçevenin içine yazmak |
| `taze` | aracı | alan uç | sürüm ya da sayaç alanı |
| `yonlendirilebilir` | aracı | bağlantı kurulumu | açılışta bir kez, sonra sabit |

Tablonun sağ sütunu, WebSocket kullanan bir uygulamanın **yeniden yazmak zorunda kaldığı**
şeydir. HTTP'de bu kararların hepsi belirtimin içinde tanımlıydı ve yolun üzerindeki
herhangi bir aracı onları uygulayabiliyordu. Çerçeve biçiminde tanımlı olan tek şey
taşımadır; anlamı taşıyan alanları iki uç kendi aralarında kararlaştırır.

Bunun bir kusur olmadığını da kayda geçirmek gerekir. Ölçüm tablosunda tünel satırının
`yanlış` sütunu **sıfırdır**. Aracı hiçbir yanlış karar almaz, çünkü hiç karar almaz. Açık
rejimin beş yanlışı, bildirime inanmanın bedeliydi; tünel o bedeli de ödemez. Seçim, kararı
yolun üzerinde ucuza almakla, kararı uçta pahalıya ama tam almak arasındadır.

#### Özet

- Yükseltme el sıkışması olağan bir HTTP alışverişidir; `101 Switching Protocols` satırı
  bir sonucu değil, bağlantı üzerinde artık başka bir protokol konuşulacağını bildirir.
- Aynı kırk alışveriş açık rejimde 195 doğru ve 5 yanlış kararla, hiç tur borcu doğurmadan
  ölçülürken tünel rejiminde 200 kararın 200'ü alınamaz ve kırk tur borcu doğar.
- Aracının bağlantı boyunca okuyabildiği tek ileti yükseltme isteğidir ve o da beş kararın
  yalnız ikisini verir; 101 yanıtı ile çerçevelerin hiçbiri karar üretmez.
- Çerçeve başlığı iki ile on dört bayt arasındadır ve taşıdığı alanlar — FIN, işlem kodu,
  maske, uzunluk — taşıma hakkındadır; bayt kısmak karar kaybettirmez, alanın yok olması
  kaybettirir.
- Taşınan iki yüz karar uçların işine dönüşür; buna karşılık tünel rejiminde alınan yanlış
  karar sayısı sıfırdır.

#### Sonraki Adım

Tünel, aracıyı iletiden tümüyle kopardığı için iki yüz kararın tamamını uçlara taşıdı.
Ama sunucunun istemciye kendiliğinden veri göndermesi için bağlantının biçimini değiştirmek
zorunlu mudur? Yanıtın gövdesi bir kerede yazılmak zorunda değildir: açık tutulup üzerine
parça parça yazılabilir ve bu, HTTP'nin kendi kuralları içinde kalır. Sonraki ders bu
seçeneği ölçer — aracı iletiyi hâlâ okuyabildiğinde elinde kaç karar kalır ve okuyabilmenin
kendisi ne getirir.

### Sunucu Gönderimli Olaylar

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/sunucu-gonderimli-olaylar

Akışın HTTP kalmasının aracıya ne bıraktığı: baş iletinin taşıdığı beş karar, olay sayısı arttıkça karar noktasının seyrelmesi, bir kez alınan kararın gövdenin tamamına uygulanmasının doğurduğu yanlış ve tamponlayan bir aracının olayları ne kadar geciktirdiği.

Önceki ders tünelin bedelini saydı: yükseltmeden sonra aracı iki yüz kararın tamamını
kaybediyor, kırk alışverişin kırkı tur borcuna dönüşüyordu. Ama sunucunun istemciye
kendiliğinden veri göndermesi için bağlantının biçimini değiştirmek zorunlu değildir. HTTP
yanıtının gövdesinin tek seferde yazılması hiçbir yerde şart koşulmaz: gövde açık tutulup
üzerine parça parça yazılabilir.

**Sunucu gönderimli olaylar (Server-Sent Events, SSE)** bu seçeneği bir biçime bağlar. Tek
bir GET isteğine verilen tek bir yanıt açık tutulur ve sunucu ona sırayla olay yazar. Bu
dersin ölçüsü şudur: akış **HTTP kaldığı için** aracı iletiyi hâlâ okuyabilir — peki elinde
kaç karar kalır ve okuyabilmenin karşılığında ne öder?

#### Akış Yanıtı

İstek olağandır. Yanıt da olağandır; onu ayıran tek şey **ortam türü (media type)** ve
gövdenin bitmemesidir.

```text
GET /akis/olcum HTTP/1.1
Host: istasyon.ornek
Accept: text/event-stream

HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-store
Transfer-Encoding: chunked

event: olcum
id: 17
data: {"istasyon": "kuzey-yamac", "deger": 3}

: yorum satırı hiçbir olay üretmez

event: olcum
id: 18
data: {"istasyon": "kuzey-yamac", "deger": 5}
```

Gövde satır tabanlıdır: alan satırları bir olayı kurar, boş satır onu kapatır, iki nokta
ile başlayan satır yorumdur. Uzunluk önceden bilinemediği için gövde **parçalı aktarım
(chunked transfer)** ile ya da bağlantının kapanmasıyla sonlanır. Alan biçiminin ayrıntısı,
`id` alanına dayanan yeniden bağlanma ve sunucu tarafının bağlantı başına ödediği maliyet
**Önbellekleme, Kuyruklar ve Eşzamansız İşleme** kursunda ölçüldü. **Burada ölçülen,
aradaki aracının bu iletiden alabildiği karardır.**

#### Karar Noktası

- **DG6.** Önceki derste bir bağlantı üzerinde taşınan kırk alışveriş, burada bir akışın
  gövdesinde taşınır. `olay`, tek bir akışın gövdesine yazılan alışveriş sayısıdır;
  `olay = 1` her alışverişin kendi isteğini açtığı istek–yanıt biçimidir.
- **DG7.** Aracı kararını akışın **baş iletisinden** alır. Gövdeye yazılan olaylar kendi
  istek satırını ve başlık alanlarını taşımaz; aracı için görünmezdirler.
- **DG8.** `kapsam yanlışı` sütunu ikinci bir okumadır: baş iletiden alınan karar
  gövdedeki **her olaya** uygulandığında kaç kararın yanlış olduğunu sayar.
- **DG9.** Son satır, iletinin hiçbir alanının okunamadığı tünel rejimidir; önceki dersin
  ölçümüyle karşılaştırma için buraya alınmıştır.

```python
TOHUM = 20260809
GUVENLI = {"GET", "HEAD"}
ETKISIZ = {"GET", "HEAD", "PUT", "DELETE"}
YONTEMLER = ["GET", "GET", "GET", "HEAD", "POST", "PUT", "DELETE"]
YOLLAR = ["/olcum/kuzey", "/olcum/yamac", "/ozet", "/kayit", "/oturum"]
KARARLAR = ("saklanabilir", "paylasilabilir", "taze", "yinelenebilir",
            "yonlendirilebilir")
ACIK = ("yontem", "yol", "konak", "ozel_imi", "dogrulayici")


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def alisverisler(sayi: int = 40) -> list[dict]:
    r, liste = uretec(TOHUM), []
    for i in range(sayi):
        yontem, yol = YONTEMLER[r(7)], YOLLAR[r(5)]
        ozel = yol == "/oturum" or r(5) == 0
        degisti = r(3) == 0
        liste.append({"no": i + 1, "yontem": yontem, "yol": yol, "ozel": ozel,
                      "degisti": degisti, "ozel_imi": ozel and r(4) != 0,
                      "damga_atladi": degisti and r(6) == 0})
    return liste


def kahin(a: dict) -> dict:
    return {"saklanabilir": a["yontem"] in GUVENLI and not a["ozel"],
            "paylasilabilir": not a["ozel"], "taze": not a["degisti"],
            "yinelenebilir": a["yontem"] in ETKISIZ, "yonlendirilebilir": True}


def ileti(a: dict, gorunur: tuple) -> dict:
    tam = {"yontem": a["yontem"], "yol": a["yol"], "konak": "istasyon.ornek",
           "ozel_imi": a["ozel_imi"],
           "dogrulayici": a["degisti"] and not a["damga_atladi"]}
    return {k: v for k, v in tam.items() if k in gorunur}


def araci(i: dict) -> dict:
    k = {}
    if "yontem" in i:
        k["yinelenebilir"] = i["yontem"] in ETKISIZ
    if "ozel_imi" in i:
        k["paylasilabilir"] = not i["ozel_imi"]
        if "yontem" in i:
            k["saklanabilir"] = i["yontem"] in GUVENLI and not i["ozel_imi"]
    if "dogrulayici" in i:
        k["taze"] = not i["dogrulayici"]
    if "konak" in i:
        k["yonlendirilebilir"] = True
    return k


def olc_akis(olay: int, gorunur: tuple = ACIK) -> tuple:
    liste = alisverisler()
    nokta = dogru = yanlis = alinamayan = tur = bayt = kapsam = 0
    for i, a in enumerate(liste):
        m = ileti(liste[i - i % olay], gorunur)     # akışı açan iletinin alanları
        bastan, gercek = araci(m), kahin(a)
        kapsam += sum(bastan[k] != gercek[k] for k in bastan)
        verilen = bastan if i % olay == 0 else {}   # karar yalnız akış başında alınır
        dogru += sum(verilen[k] == gercek[k] for k in verilen)
        yanlis += sum(verilen[k] != gercek[k] for k in verilen)
        alinamayan += len(KARARLAR) - len(verilen)
        tur += 1 if len(verilen) < len(KARARLAR) else 0
        if i % olay == 0:
            nokta += 1
            bayt += sum(len(k) + len(str(v)) + 4 for k, v in m.items())
    return nokta, dogru, yanlis, alinamayan, tur, bayt, kapsam


bicim = "{:>5s} {:>6d} {:>6d} {:>7d} {:>11d} {:>4d} {:>6d} {:>14d}"
print(f"{'olay':>5s} {'nokta':>6s} {'doğru':>6s} {'yanlış':>7s} "
      f"{'alınamayan':>11s} {'tur':>4s} {'bayt':>6s} {'kapsam yanlışı':>14s}")
for olay in (1, 2, 4, 8, 20, 40):
    print(bicim.format(str(olay), *olc_akis(olay)))
print(bicim.format("-", *olc_akis(40, gorunur=())))

liste = alisverisler()
bas = araci(ileti(liste[0], ACIK))
print()
print("olay=40 kapsamında karar başına yanlış (40 olay üzerinden):")
for k in KARARLAR:
    y = sum(bas[k] != kahin(a)[k] for a in liste)
    print(f"  {k:<18s} baş iletideki değer {str(bas[k]):<5s} yanlış {y:2d}/40")
```

```
 olay  nokta  doğru  yanlış  alınamayan  tur   bayt kapsam yanlışı
    1     40    195       5           0    0   3524              5
    2     20     97       3         100   20   1747             37
    4     10     47       3         150   30    879             45
    8      5     23       2         175   35    436             57
   20      2      9       1         190   38    179             71
   40      1      4       1         195   39     87             73
    -      1      0       0         200   40      0              0

olay=40 kapsamında karar başına yanlış (40 olay üzerinden):
  saklanabilir       baş iletideki değer False yanlış 13/40
  paylasilabilir     baş iletideki değer True  yanlış 17/40
  taze               baş iletideki değer True  yanlış 12/40
  yinelenebilir      baş iletideki değer False yanlış 31/40
  yonlendirilebilir  baş iletideki değer True  yanlış  0/40
```

İlk satır istek–yanıt biçimidir ve kursun ölçtüğü açık rejimin ta kendisidir: 195 doğru,
5 yanlış, hiç alınamayan karar yok, hiç tur borcu yok, 3524 bayt. Son satır önceki dersin
tünel rejimidir: 0 karar, 200 alınamayan, 40 tur borcu.

Aradaki satırlar akışın ne yaptığını gösterir. Kırk olay tek bir gövdeye yazıldığında aracı
**bir** karar noktasında kalır ve elinde **beş** karar bulunur. Bu, tünelin sıfırından beş
karar fazladır ve fark tam olarak şuradan gelir: akışı açan yanıt **okunabilir bir HTTP
yanıtıdır.** Önceki derste 101 yanıtı kişiye özel imi de doğrulayıcı da taşımadığı için
sıfır karar veriyordu; burada yanıt olağan bir 200'dür ve beş kararın beşini de besler.

Baytın düşüşü de aynı yerden gelir. 3524 bayttan 87 bayta inen şey, kırk iletinin başlık
alanlarının otuz dokuzunun hiç gönderilmemesidir. Alan gitmiş, karar da gitmiştir; bu,
önceki dersin sonucuyla aynı yöndedir. **Bayt ile karar birlikte düşer, çünkü ikisini de
taşıyan şey alandır.**

#### Kararın Kapsamı

Karar noktasının seyrelmesi kayıpların tamamı değildir. Bir aracı, akışın başında aldığı
kararı unutmaz — onu **gövdenin tamamına** uygular. Yanıtı saklanabilir saydıysa gövdenin
tümünü saklar; taze saydıysa gövdenin tümünü taze sayar.

`kapsam yanlışı` sütunu bunu ölçer. `olay = 1` iken baş ileti ile olay aynı şeydir ve
sütun beş yanlışı verir. `olay = 40` iken tek bir karar kümesi kırk olaya uygulanır ve
iki yüz kararın **73'ü** yanlış çıkar. Karar noktası birden kırka çıkmamıştır; **kararın
kapsamı** bire kırktan yayılmıştır.

Alt döküm 73'ün nereden geldiğini söyler. `yonlendirilebilir` hiç yanılmaz, çünkü değeri
her alışverişte aynıdır; kapsamın genişlemesi **değişmeyen** bir kararı bozmaz.
`yinelenebilir` ise 31 kez yanılır: baş ileti bir POST'tur ve kararı `False` çıkmıştır, oysa
gövdedeki olayların çoğu güvenli yöntemlere aittir. Kalan üçü — `saklanabilir` 13,
`paylasilabilir` 17, `taze` 12 — kişiye özellik ve bayatlığın olaydan olaya değişmesinden
gelir. Kural şudur: **kapsamın genişlemesi, yalnız alışverişten alışverişe değişen
kararları bozar.**

Bu, akışın en sessiz maliyetidir. Karar noktasında ölçüldüğünde aracının yalnız **bir**
yanlışı vardır ve bu, açık rejimin beş yanlışından azdır. Aynı akış kapsamında ölçüldüğünde
yanlış sayısı beşten 73'e çıkar. Kümenin çözünürlüğü 1/200 = 0,0050 olduğuna göre bu fark
ölçülebilir bandın çok üstündedir ve yorum farkından değil, **kararın ne kadar süre geçerli
sayıldığından** gelir. Akış yanıtının önbelleklenmeye kapatılmasının nedeni budur.

#### Yeniden Bağlanma Bir Karar Noktasıdır

Tablodaki `olay` sütunu tek bir soruyu daha yanıtlar. Bir akış hiçbir zaman sonsuza kadar
ayakta kalmaz; bağlantı kopar, istemci yeniden bağlanır. Yeniden bağlanma yeni bir istek,
yeni bir istek ise **yeni bir karar noktasıdır.** Yirmi olayda bir kopan bir akış tablonun
`olay = 20` satırıdır: iki karar noktası, on karar. Sekiz olayda bir kopan akış beş nokta ve
yirmi beş karar verir. Aracının gördüğü şey, akışın ne kadar kararlı olduğuyla ters
orantılıdır.

Bunun bir sonucu yönlendirmedir. `yonlendirilebilir` kararı akışın başında bir kez alınır
ve akış boyunca **dondurulur**. İstek–yanıt biçiminde aracı kırk alışverişin her birini
ayrı ayrı yönlendirebilir; bir üst kaynak yanıt vermediğinde sonraki isteği başka bir yere
gönderir. Tek bir akışta bu esneklik yoktur: seçim bir kez yapılmıştır ve değiştirmenin tek
yolu akışı kesmektir. Kesmek de yeni bir karar noktası doğurur, yani aracının yönlendirmeyi
düzeltmesi ancak istemcinin yeniden bağlanmasıyla mümkün olur.

Kopmanın ucuz olmasını sağlayan alan — istemcinin en son gördüğü olay kimliğini yeni isteğe
iliştirmesi — bu tabloda hiçbir karar üretmez. Aracı o alanı okur ama ondan çıkaracağı bir
şey yoktur; alan uçlar arasındaki bir sözleşmedir. Bu, kursun dört numaralı okumasının bir
başka örneğidir: **bir alanın var olması, aracının ondan karar çıkarabildiği anlamına
gelmez.**

#### Tamponlama

Aracı iletiyi okuyabildiği için gövdeyi de görür ve gördüğü şey üzerinde bir seçim yapar:
baytı hemen mi iletsin, yoksa biriktirip toplu mu göndersin. Biriktirmek olağan bir
verimlilik kuralıdır; bitmeyen bir gövdede ise olayları geciktirir.

- **DG10.** Aracı, tamponu eşiğe ulaşmadan gövdeden hiçbir bayt iletmez. Eşik 0 ise
  tamponlama yoktur. Olay metinleri belirtimin satır biçimiyle üretilir, değerler kurgudur.

```python
def olay_metni(no: int, deger: int) -> str:
    """Belirtimin satır biçimi: alan satırları ve olayı kapatan boş satır."""
    return (f"event: olcum\nid: {no}\n"
            f'data: {{"istasyon": "kuzey-yamac", "deger": {deger}}}\n\n')


OLAYLAR = [olay_metni(i + 1, i * 3 % 97) for i in range(40)]


def tamponla(esik: int) -> tuple:
    tampon, grup, bekleme = 0, [], []
    for i, m in enumerate(OLAYLAR):
        tampon += len(m)
        grup.append(i)
        if tampon >= esik:
            bekleme.extend(i - j for j in grup)   # her olayın kaç olay beklediği
            tampon, grup = 0, []
    ortalama = sum(bekleme) / len(bekleme) if bekleme else 0.0
    return len(bekleme), max(bekleme, default=0), ortalama, len(grup)


print(f"olay sayısı {len(OLAYLAR)}, olay boyu "
      f"{min(len(m) for m in OLAYLAR)}-{max(len(m) for m in OLAYLAR)} bayt, "
      f"gövde {sum(len(m) for m in OLAYLAR)} bayt")
print()
print(f"{'eşik':>6s} {'iletilen':>9s} {'en çok bekleme':>15s} "
      f"{'ortalama bekleme':>17s} {'sonda tutulan':>14s}")
for esik in (0, 128, 256, 1024, 4096):
    iletilen, en_cok, ortalama, tutulan = tamponla(esik)
    ec = str(en_cok) if iletilen else "-"
    ort = f"{ortalama:.2f}" if iletilen else "-"
    print(f"{esik:6d} {iletilen:9d} {ec:>15s} {ort:>17s} {tutulan:14d}")
```

```
olay sayısı 40, olay boyu 66-68 bayt, gövde 2704 bayt

  eşik  iletilen  en çok bekleme  ortalama bekleme  sonda tutulan
     0        40               0              0.00              0
   128        40               1              0.50              0
   256        40               3              1.50              0
  1024        32              15              7.50              8
  4096         0               -                 -             40
```

İki bakışla okunur. İlk üç satırda gecikme eşikle orantılı büyür: 256 baytlık bir tampon
olayları ortalama 1,5 olay geciktirir. Son satır ise farklı bir şeydir. Akışın kırk olayda
ürettiği toplam gövde 2704 bayttır ve 4096 baytlık bir eşik bu gövdenin tamamından
büyüktür; aracı hiçbir baytı iletmez ve akış, kapanana kadar istemci için **hiç var
olmamıştır.**

Kritik olan şudur: aracı yanlış bir şey yapmıyor. Bir yanıtı biriktirmek, bitmiş bir gövde
için doğru davranıştır. Aracının bilmediği şey gövdenin bitmeyeceğidir ve bunu ona söyleyen
tek alan **ortam türüdür**. `text/event-stream` değerini tanımayan bir aracının elinde
başka hiçbir im yoktur: gövde uzunluğu bildirilmemiştir, yöntem GET'tir, durum kodu 200'dür
ve bunların hiçbiri "bu gövde bitmeyecek" demez. Sunucu tarafının kalp atışı satırıyla
aldığı önlem, aynı boşluğun öbür uçtan kapatılmasıdır ve o kurs içinde ölçülmüştü.

#### Tek Yön

Akış tek yönlüdür: sunucudan istemciye. İstemcinin sunucuya söyleyeceği bir şey olduğunda
akışa yazamaz, olağan bir istek açar. Bu, ölçüm tablosunda kayıp gibi görünmeyen bir
ayrıntıdır ama sonucu doğrudandır: **istemciden gelen her ileti yeniden bir karar
noktasıdır.** Akış tarafında karar noktası bire inmiştir; istemcinin sunucuya yazdığı her
istek ise ölçüm tablosunun ilk satırındaki gibi okunabilir bir alışveriştir ve kendi karar
noktasını geri getirir.

Tünelde bu bile yoktur; oradaki her iki yönlü ileti aynı çerçeve biçimindedir ve hiçbiri
karar üretmez. Aradaki fark, seçimin ne üzerinden yapıldığını da gösterir: iki yön gerçekten
gerekliyse tünel, gereken şey yalnız sunucu akışıysa SSE. Yönün bedeli karar cinsinden
ölçülebilir.

#### Özet

- Sunucu gönderimli olaylar HTTP'nin içinde kalır: istek olağan bir GET, yanıt olağan bir
  200'dür ve gövde açık tutulup üzerine olay yazılır.
- Kırk olay tek bir akışta taşındığında aracının karar noktası bire iner ve elinde beş
  karar kalır; tünel rejiminde bu sayı sıfırdır ve fark, akışı açan yanıtın okunabilir
  olmasından gelir.
- Baş iletiden alınan karar gövdenin tamamına uygulanır; karar noktasında ölçülen yanlış
  sayısı 1 iken kapsamda ölçülen yanlış sayısı iki yüz kararın 73'üdür.
- Bayt 3524'ten 87'ye inerken karar da 200'den 5'e iner; ikisini de taşıyan şey alandır.
- Tamponlayan bir aracı 256 baytlık eşikte olayları ortalama 1,5 olay geciktirir; 2704
  baytlık gövdeye karşılık 4096 baytlık bir eşikte hiçbir olay iletilmez ve gövdenin
  bitmeyeceğini söyleyen tek alan ortam türüdür.

#### Sonraki Adım

Akış da tünel de bağlantıyı **açık tutarak** çalışıyor: biri gövdeyi bitirmiyor, öbürü
protokolü değiştiriyor. İkisi de sunucunun elini serbest bırakmak için bağlantının süresini
uzatıyor. Peki hiçbirini yapmadan, protokole tek bir yeni alan bile eklemeden, yalnız daha
sık sorarak nereye kadar gidilir? Sonraki ders bunu ölçer: kısa yoklama, uzun yoklama ve
sunucu akışı aynı olay kümesi üzerinde yan yana konur ve bölen olay değil, **karar**
olur.

### Uzun ve Kısa Yoklama

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/uzun-ve-kisa-yoklama

Gerçek zamanlılığın tur satın alınarak taklit edilmesi: yoklamanın protokole tek bir yeni alan eklememesi, karar başına tur maliyetinin dört yaklaşımda da değişmemesi ve harcanan tur ile tur borcunun toplamının sabit kalması.

Önceki iki ders bağlantıyı **açık tutan** iki yol ölçtü. Tünel protokolü değiştiriyor,
akış gövdeyi bitirmiyordu; ikisi de sunucunun kendiliğinden konuşabilmesi için bağlantının
süresini uzatıyordu. Bu dersin sorusu şudur: hiçbirini yapmadan, protokole tek bir yeni
alan bile eklemeden, yalnız **daha sık sorarak** nereye kadar gidilir?

**Yoklama (polling)**, istemcinin durumu öğrenmek için düzenli aralıklarla istek
göndermesidir. **Kısa yoklamada** sunucu hemen yanıt verir; verecek yeni bir şeyi yoksa
onu söyler. **Uzun yoklamada** sunucu yanıtı geciktirir; bir değişiklik olana ya da tutma
süresi dolana kadar isteği elinde tutar. Bu dersin ölçüsü ikisinin **karar başına ödettiği
turdur.** Bir uyarı: önceki derste geçen `yokla` denetim çerçevesi (ping) ile buradaki
yoklama (polling) ayrı şeylerdir; biri bağlantının ayakta olduğunu doğrulayan bir çerçeve,
öbürü uygulamanın durumu öğrenmek için attığı turdur.

#### Aynı İstek, Farklı Zaman

İki yoklama biçimi arasındaki farkın **iletide bir karşılığı yoktur.**

```text
# kısa yoklama — sunucu hemen yanıtlar
GET /olcum/kuzey HTTP/1.1
Host: istasyon.ornek
If-None-Match: "s-118"

HTTP/1.1 304 Not Modified
ETag: "s-118"

# uzun yoklama — istek satırı ve başlıkları aynı, yanıt geciktirilir
GET /olcum/kuzey HTTP/1.1
Host: istasyon.ornek
If-None-Match: "s-118"

HTTP/1.1 200 OK
ETag: "s-119"
Content-Type: application/json

{"istasyon": "kuzey-yamac", "deger": 5}
```

İki istek birbirinin aynıdır. Yeni bir yöntem, yeni bir başlık alanı, yeni bir durum kodu
yoktur. Koşullu istek alanı da yoklamanın icadı değildir; HTTP'nin kendi alanıdır ve bu
kursun HTTP ailesi konusunda kurulmuştur. Farkın tamamı **sunucunun ne zaman yazdığıdır** ve
zaman iletide yazan bir şey değildir.

Yoklamanın sunucuya düşen maliyeti — istek sayısı, boş yanıt oranı, tutulan bağlantı —
**Önbellekleme, Kuyruklar ve Eşzamansız İşleme** kursunda ölçüldü. **Burada bölen olay
değil karardır ve ölçülen, aradaki aracının hesabıdır.**

#### Ölçüm

- **DG11.** Kırk tik boyunca istasyonun durumu izlenir. Bir tikte durumun değişip
  değişmediği kurgunun kâhinindedir; kırk tikin on ikisinde durum değişir.
- **DG12.** Bir **tur**, istemcinin istek gönderip yanıt aldığı bir gidiş–dönüştür. Yanıtın
  boş olması turu ucuzlatmaz.
- **DG13.** Kısa yoklama her `aralık` tikte bir sorar. Uzun yoklama değişiklik olana kadar
  tutar; `tutma` tik dolarsa boş yanıt verir ve istemci yeniden bağlanır.
- **DG14.** Sunucu akışı ile tünel tek bir tur harcar; sonrasında aracının okuyabileceği
  yeni bir ileti yoktur.
- **DG15.** Alınamayan her beş karar bir **tur borcudur**: aracı o alışverişin kararını
  almak isterse sunucuya dönmek zorundadır.

```python
TOHUM = 20260809
GUVENLI = {"GET", "HEAD"}
ETKISIZ = {"GET", "HEAD", "PUT", "DELETE"}
YONTEMLER = ["GET", "GET", "GET", "HEAD", "POST", "PUT", "DELETE"]
YOLLAR = ["/olcum/kuzey", "/olcum/yamac", "/ozet", "/kayit", "/oturum"]
KARARLAR = ("saklanabilir", "paylasilabilir", "taze", "yinelenebilir",
            "yonlendirilebilir")
ACIK = ("yontem", "yol", "konak", "ozel_imi", "dogrulayici")


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def alisverisler(sayi: int = 40) -> list[dict]:
    r, liste = uretec(TOHUM), []
    for i in range(sayi):
        yontem, yol = YONTEMLER[r(7)], YOLLAR[r(5)]
        ozel = yol == "/oturum" or r(5) == 0
        degisti = r(3) == 0
        liste.append({"no": i + 1, "yontem": yontem, "yol": yol, "ozel": ozel,
                      "degisti": degisti, "ozel_imi": ozel and r(4) != 0,
                      "damga_atladi": degisti and r(6) == 0})
    return liste


def kahin(a: dict) -> dict:
    return {"saklanabilir": a["yontem"] in GUVENLI and not a["ozel"],
            "paylasilabilir": not a["ozel"], "taze": not a["degisti"],
            "yinelenebilir": a["yontem"] in ETKISIZ, "yonlendirilebilir": True}


def ileti(a: dict, gorunur: tuple) -> dict:
    tam = {"yontem": a["yontem"], "yol": a["yol"], "konak": "istasyon.ornek",
           "ozel_imi": a["ozel_imi"],
           "dogrulayici": a["degisti"] and not a["damga_atladi"]}
    return {k: v for k, v in tam.items() if k in gorunur}


def araci(i: dict) -> dict:
    k = {}
    if "yontem" in i:
        k["yinelenebilir"] = i["yontem"] in ETKISIZ
    if "ozel_imi" in i:
        k["paylasilabilir"] = not i["ozel_imi"]
        if "yontem" in i:
            k["saklanabilir"] = i["yontem"] in GUVENLI and not i["ozel_imi"]
    if "dogrulayici" in i:
        k["taze"] = not i["dogrulayici"]
    if "konak" in i:
        k["yonlendirilebilir"] = True
    return k


def kisa(aralik: int, liste: list[dict]) -> list[int]:
    """Her `aralik` tikte bir sorulur; yanıt o tikteki durumu döndürür."""
    return list(range(aralik - 1, len(liste), aralik))


def uzun(tutma: int, liste: list[dict]) -> list[int]:
    """Sunucu değişiklik olana kadar tutar; `tutma` tik dolarsa boş yanıt verir."""
    turlar, t = [], 0
    while t < len(liste):
        son = min(t + tutma - 1, len(liste) - 1)
        for u in range(t, son + 1):
            if liste[u]["degisti"]:
                son = u
                break
        turlar.append(son)
        t = son + 1
    return turlar


def olc(turlar: list[int], gorunur: tuple = ACIK) -> tuple:
    liste = alisverisler()
    dogru = yanlis = bayt = 0
    alanlar = set()
    for t in turlar:
        m = ileti(liste[t], gorunur)
        verilen, gercek = araci(m), kahin(liste[t])
        dogru += sum(verilen[k] == gercek[k] for k in verilen)
        yanlis += sum(verilen[k] != gercek[k] for k in verilen)
        bayt += sum(len(k) + len(str(v)) + 4 for k, v in m.items())
        alanlar |= set(m)
    alinan = dogru + yanlis
    oran = len(turlar) / alinan if alinan else float("nan")
    return (len(turlar), len(alanlar), alinan, dogru, yanlis,
            200 - alinan, oran, bayt)


LISTE = alisverisler()
print(f"tik {len(LISTE)}, karar kümesi {len(LISTE) * len(KARARLAR)}, "
      f"değişim {sum(a['degisti'] for a in LISTE)}")
print()
bicim = "{:<18s} {:>4d} {:>5d} {:>7d} {:>6d} {:>7d} {:>11d} {:>10.3f} {:>6d}"
print(f"{'yaklaşım':<18s} {'tur':>4s} {'alan':>5s} {'alınan':>7s} "
      f"{'doğru':>6s} {'yanlış':>7s} {'alınamayan':>11s} {'tur/karar':>10s} "
      f"{'bayt':>6s}")
YAKLASIMLAR = [("kısa yoklama (1)", kisa(1, LISTE), ACIK),
               ("kısa yoklama (4)", kisa(4, LISTE), ACIK),
               ("uzun yoklama (5)", uzun(5, LISTE), ACIK),
               ("sunucu akışı", [0], ACIK),
               ("tünel", [0], ())]
for ad, turlar, gorunur in YAKLASIMLAR[:-1]:
    print(bicim.format(ad, *olc(turlar, gorunur)))
n, alan, alinan, d, y, e, oran, b = olc([0], gorunur=())
print(f"{'tünel':<18s} {n:4d} {alan:5d} {alinan:7d} {d:6d} {y:7d} {e:11d} "
      f"{'-':>10s} {b:6d}")

print()
print(f"{'yaklaşım':<18s} {'harcanan tur':>12s} {'tur borcu':>9s} "
      f"{'toplam':>6s}")
for ad, turlar, gorunur in YAKLASIMLAR:
    olculen = olc(turlar, gorunur)
    harcanan, borc = olculen[0], olculen[5] // len(KARARLAR)
    print(f"{ad:<18s} {harcanan:12d} {borc:9d} {harcanan + borc:6d}")
```

```
tik 40, karar kümesi 200, değişim 12

yaklaşım            tur  alan  alınan  doğru  yanlış  alınamayan  tur/karar   bayt
kısa yoklama (1)     40     5     200    195       5           0      0.200   3524
kısa yoklama (4)     10     5      50     49       1         150      0.200    896
uzun yoklama (5)     15     5      75     72       3         125      0.200   1317
sunucu akışı          1     5       5      4       1         195      0.200     87
tünel                 1     0       0      0       0         200          -      0

yaklaşım           harcanan tur tur borcu toplam
kısa yoklama (1)             40         0     40
kısa yoklama (4)             10        30     40
uzun yoklama (5)             15        25     40
sunucu akışı                  1        39     40
tünel                         1        40     41
```

İlk satır kursun açık rejiminin ta kendisidir: kırk tur, 195 doğru, 5 yanlış, hiç
alınamayan karar yok, 3524 bayt. Aralığı dörde çıkarmak turu ona indirir ve alınan kararı
elliye düşürür. Uzun yoklama on iki değişim için on iki tur harcar, üç turu da tutma
süresinin dolmasına verir: toplam on beş.

#### Turun Sabiti

`alan` sütunu dört HTTP yaklaşımında da **beştir.** Yoklama protokole hiçbir alan
eklemez — ne kısa biçimi, ne uzun biçimi. Sunucu akışı da eklemez. Ekleyen tek şey
**turdur** ve turun kaç tane olduğu iletide yazmaz.

`tur/karar` sütunu bunun doğrudan sonucudur: dört yaklaşımda da **0,200.** Bu sayı
1/5'tir ve tesadüf değildir. Okunabilir bir alışveriş beş alan taşır, beş alan beş karar
verir; dolayısıyla bir tur her zaman tam beş karar satın alır. Yoklamanın sıklığını
değiştirmek bu oranı **oynatamaz**, çünkü oranın payı da paydası da aynı alan kümesinden
gelir.

Buradan kursun ikinci iddiasının üçüncü biçimi çıkar. Alan eklemek karar kazandırmıyordu;
alanı gizlemek kararı götürüyordu; şimdi görülüyor ki **tur eklemek de karar başına maliyeti
değiştirmiyor.** Yoklama, gerçek zamanlılığı taklit ederken protokolden hiçbir şey almaz;
yalnız aynı işlemi daha çok kez yapar.

Tünel satırı bu tablonun dışındadır ve oran hesaplanamaz: alan sıfır olduğunda bölen de
sıfır olur. Bir turun satın aldığı karar sayısı sıfırsa, tur satın almanın anlamı da kalmaz.

#### Tur Muhasebesi

İkinci tablo aynı kırk alışverişi turun iki türüne böler. **Harcanan tur**, istemcinin
gerçekten attığı gidiş–dönüştür. **Tur borcu**, aracının alamadığı kararlar için sunucuya
dönmek zorunda kalacağı gidiş–dönüştür. Dört HTTP yaklaşımında toplam **kırktır** ve
değişmez.

Bu, kursun ekseninin en yalın biçimidir. Kırk alışverişin kararı bir yerden gelmek
zorundadır: ya istemci turu peşin öder ve karar iletide gelir, ya da tur borç yazılır ve
karar alınamaz. Kısa yoklama borcu sıfıra indirir, kırk turu peşin öder. Sunucu akışı bir
tur öder, otuz dokuz borç bırakır. Aradaki her yaklaşım bu doğrunun üzerinde bir noktadır.

Tünel satırı kırk bir verir. Fazladan bir tur, yükseltme el sıkışmasının kendisidir: bir
tur harcanmıştır ve karşılığında hiçbir karar alınmamıştır. Kırk alışverişin kırkı da borç
kalır. Gizlemenin bedeli, tablonun tek satır dışına taşan yerinde durur.

#### Tutulan İsteğin Aracıdaki Karşılığı

Uzun yoklamanın aracı için bir yan etkisi vardır. Sunucu isteği elinde tutarken bağlantı
üzerinde hiçbir bayt akmaz; aracının gördüğü şey, yanıtı gecikmiş bir istektir. Aracının
elinde bu bekleyişin **kasıtlı** olduğunu söyleyen hiçbir alan yoktur — istek satırı
olağan bir GET'tir, başlıklarda "beni bekleteceksin" diyen bir alan yoktur. Önceki derste
geçen boşta bekleme süresi ise bir aracının olağan kuralıdır ve tutulan isteği,
bekletildiğini bilmeden keser.

- **DG16.** Aracı, üzerinde bayt akmayan bir bağlantıyı boşta bekleme süresi eşiğini
  aştığında keser; istemci kesilen turu yeniden açar. Tablodaki `boşta kesme` sütunu bu
  eşiği tik cinsinden verir.

```python
TOHUM = 20260809
YOLLAR = ["/olcum/kuzey", "/olcum/yamac", "/ozet", "/kayit", "/oturum"]


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def degisimler(sayi: int = 40) -> list[bool]:
    """Önceki bloğun üretecinden yalnız `degisti` imi alınır; çağrı sırası korunur."""
    r, liste = uretec(TOHUM), []
    for _ in range(sayi):
        r(7)                                # yöntem seçimi
        yol = YOLLAR[r(5)]
        ozel = yol == "/oturum" or r(5) == 0
        degisti = r(3) == 0
        if ozel:
            r(4)                            # kişiye özel imi
        if degisti:
            r(6)                            # damga çözünürlüğü
        liste.append(degisti)
    return liste


def uzun_turlar(tutma: int, degisti: list[bool]) -> list[tuple]:
    """Her tur (başlangıç, bitiş) tik çiftidir; bitiş ya değişim ya tutma sınırıdır."""
    turlar, t = [], 0
    while t < len(degisti):
        son = min(t + tutma - 1, len(degisti) - 1)
        for u in range(t, son + 1):
            if degisti[u]:
                son = u
                break
        turlar.append((t, son))
        t = son + 1
    return turlar


TURLAR = uzun_turlar(5, degisimler())
SURELER = [s - b + 1 for b, s in TURLAR]
print(f"uzun yoklama (tutma 5): {len(TURLAR)} tur, süreler {SURELER}")
print()
print(f"{'boşta kesme':>11s} {'kesilen tur':>11s} {'toplam tur':>10s} "
      f"{'alınan karar':>12s} {'tur/karar':>9s}")
for esik in (1, 2, 3, 5, 10):
    kesilen = sum(1 for s in SURELER if s > esik)
    toplam = len(TURLAR) + kesilen
    print(f"{esik:11d} {kesilen:11d} {toplam:10d} {toplam * 5:12d} "
          f"{toplam / (toplam * 5):9.3f}")
```

```
uzun yoklama (tutma 5): 15 tur, süreler [2, 5, 5, 3, 5, 1, 1, 2, 1, 3, 4, 4, 1, 1, 2]

boşta kesme kesilen tur toplam tur alınan karar tur/karar
          1          10         25          125     0.200
          2           7         22          110     0.200
          3           5         20          100     0.200
          5           0         15           75     0.200
         10           0         15           75     0.200
```

`alınan karar` sütunu turun beşle çarpımıdır; bir önceki ölçüm okunabilir her alışverişin
tam beş karar verdiğini göstermişti. Bir tiklik bir kesme eşiği on beş turun onunu keser ve
toplamı yirmi beşe çıkarır. Uzun yoklamanın kazandırdığı tur böylece geri alınır: on beş
turla iş görecek bir düzen, aracının eşiği yüzünden yirmi beş tur harcar. Tutma süresini
eşiğin altına çekmek çözer — beş tiklik eşikte hiçbir tur kesilmez — ama bu, uygulamanın
kendi tasarımını **yolun üzerindeki bir ayara göre** seçmesi demektir.

`tur/karar` sütunu burada da 0,200'de durur. Kesilen tur da okunabilir bir alışveriştir ve
o da beş karar verir. Kesme, aracının aldığı kararın **oranını** değiştirmez; yalnız
istemcinin ödediği turu artırır.

#### Turla Satın Alınan Şey

Turun karar satın almadığı yerde ne satın aldığını da söylemek gerekir: **tazelik.** Kısa
yoklamada aralık bir tik olduğunda istemci her değişimi olduğu tikte öğrenir. Aralık dört
tik olduğunda en kötü durumda üç tik geç öğrenir. Uzun yoklama bu gecikmeyi tur harcamadan
kapatır — sunucu değişimi beklediği için yanıt tam değişim anında yazılır — ve on beş turla
kırk turun tazeliğini verir.

Bu, üç yaklaşımın gerçek ayrımıdır ve karar tablosunda hiç görünmez. Karar tablosu aracının
hesabıdır; tazelik istemcinin hesabıdır. Bir protokol seçimi ikisini birden tartar: aracının
kaç karar alabildiği ve istemcinin kaç turla ne kadar yeni bilgi aldığı. Ölçüm bandının alt
sınırı burada da geçerlidir; iki yüz kararlık bir kümede ölçülebilen en küçük fark
1/200 = 0,0050'dir ve tablodaki farkların hepsi bunun çok üstündedir.

#### Özet

- Kısa ve uzun yoklamanın istekleri birbirinin aynıdır; fark sunucunun yanıtı ne zaman
  yazdığındadır ve zaman iletide yazan bir alan değildir.
- Dört HTTP yaklaşımında da aracının okuduğu alan sayısı beştir; yoklama protokole tek bir
  yeni alan eklemez.
- Karar başına tur maliyeti dört yaklaşımda da 0,200'dür, çünkü okunabilir bir alışveriş
  her zaman tam beş karar verir; sıklığı değiştirmek bu oranı oynatmaz.
- Harcanan tur ile tur borcunun toplamı dört HTTP yaklaşımında da kırktır: kısa yoklama
  40 + 0, uzun yoklama 15 + 25, sunucu akışı 1 + 39.
- Tünel toplamı kırk bire çıkarır; fazladan tur, karşılığında hiçbir karar alınmayan
  yükseltme el sıkışmasıdır.
- Turun satın aldığı şey karar değil tazeliktir ve tazelik aracının tablosunda hiç
  görünmez.

#### Sonraki Adım

Üç yaklaşım da tek bir uygulamanın **iki ucu** arasındaydı ve hepsi aynı taşıma zarfını
kullanıyordu: aynı konak adı, aynı bağlantı, aynı beş alan. Aracının hesabı bu yüzden tek
bir sayaçta tutulabildi — hangi turun hangi alışverişe ait olduğu her zaman belliydi. Kimi
protokol ise tek bir bağlantı bile kullanmaz. Sonraki ders, iletilerin birden çok bağlantıya
dağıldığı bir protokol ailesini ölçer ve şu soruyu sorar: aracı gördüğü turu göremediği bir
turla eşleştiremiyorsa, sayacında ne kalır?

### Dosya Aktarım Protokolleri

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/dosya-aktarim-protokolleri

Denetim kanalı ile veri kanalının ayrılması: aracı iki kanalı eşleştiremediğinde hangi kararı kaybeder, hangisini yanlış alır ve bölünmenin bedeli gizlemenin bedelinden nasıl ayrılır.

Önceki üç ders gerçek zamanlılığın üç yolunu yan yana koydu ve turu karar başına ölçtü. Üçünün
ortak bir yanı vardı: hepsi **tek bir uygulamanın iki ucu** arasındaydı ve hepsi aynı **taşıma
zarfını (transport envelope)** kullanıyordu. Aracı bir tek bağlantıya bakıyor, gördüğünü ya
görüyor ya görmüyordu.

Kimi protokol böyle çalışmaz. Bir dosya aktarımı, tek bir mantıksal işi **iki ayrı bağlantıya**
bölebilir: komutlar birinde, dosyanın baytları öbüründe. Bu dersin sorusu şudur: aracı ikisini
de görüyorsa ama **hangisinin hangisine ait olduğunu** söyleyen bir alan yoksa ne kaybeder.
Kaybın gizlemekle ilgisi yoktur; bu ders o iki bedeli ayrı ayrı sayar.

- **DG41.** Kurgu M04/K03'ün ortak kurgusudur: `istasyon.ornek` ölçüm istasyonu, arada uygulamayı
  bilmeyen bir aracı. Aracı yalnız iletide yazana bakar.
- **DG42.** Ölçülen küme **40 aktarım** ve aktarım başına **5 karardır**: `yuklemedir`,
  `yeniden_denenebilir`, `olcum_dosyasi`, `surdurulur`, `tamamlandi`. Toplam 200 karar; bu
  kümede ölçülebilen en küçük fark $1/200 = 0{,}0050$'dir.
- **DG43.** Kâhin — gerçeğin kendisi — kurguyu biz ürettiğimiz için bilinir ve rejimden rejime
  değişmez. Rejim yalnız aracının **gördüğü alanları** değiştirir.
- **DG44.** Dört rejim kurulur: iki kanal açık, veri kanalı zarflı, tek kanal açık, tek kanal
  zarflı. Aktarımların kendisi dört rejimde de aynıdır.
- **DG45.** Aktarım araçlarının kullanımı burada tekrarlanmaz. Uzak kabuk, anahtar taşıma ve
  eşitleme yordamı Linux Ağ Yönetimi ve Sorun Giderme kursunda ölçüldü; buradaki ölçü **kanal
  ayrımıdır**, araç değil.

#### Denetim Kanalı ve Veri Kanalı

Dosya aktarımının klasik biçimi iki kanal tanımlar. **Denetim kanalı (control channel)** komutları
ve yanıt kodlarını taşır; oturum boyunca açık kalır. **Veri kanalı (data channel)** yalnız
dosyanın ya da dizin dökümünün baytlarını taşır; her aktarım için açılır ve aktarım bitince
kapanır. FTP bu ayrımı yapan belirtimdir.

```text
denetim kanalı — istemci ile istasyon.ornek arasında, oturum boyunca açık

  ->  USER olcum
  <-  331 parola bekleniyor
  ->  PASS ********
  <-  230 oturum açıldı
  ->  TYPE I
  <-  200 tür ikili
  ->  PASV
  <-  227 edilgen kip (a1,a2,a3,a4,p1,p2)
  ->  SIZE /olcum/kuzey.csv
  <-  213 9216
  ->  RETR /olcum/kuzey.csv
  <-  150 veri kanalı açılıyor
  <-  226 aktarım tamam

veri kanalı — ayrı bir bağlantı, aktarım süresince

  9216 bayt akar. Komut yok, yol yok, oturum imi yok; bitişte bağlantı kapanır.
```

Bu döküm çalıştırılmadı; öğretilen biçimdir. İki nokta okunmalıdır. Birincisi, veri kanalının
**nereye açılacağı denetim kanalının gövdesinde pazarlık edilir**: `PORT` ile istemci kendi
dinlediği yeri bildirir, `PASV` ile sunucu kendi açtığı yeri bildirir. İkincisi, veri kanalında
akan baytların üzerinde **hangi komuta ait olduğunu söyleyen hiçbir alan yoktur.**

Karşı uçta duran iki tasarım vardır. SFTP tek bir bağlantı kurar, o bağlantıyı bir zarfın içine
alır ve komutları da baytları da aynı bağlantı üzerinde **çerçeveler**; her isteğin bir istek imi
vardır ve yanıt o imle döner. FTPS iki kanallı düzeni korur ama her kanalı ayrı ayrı zarfa alır —
ve "ayrı ayrı" burada anahtar sözcüktür.

#### Sürdürme ve Dizin Dökümü

Ölçülen beş karardan ikisi bu iki ayrıntıdan doğuyor, o yüzden önce onlar kurulmalı.

**Sürdürme.** Yarıda kesilmiş bir aktarım iki türlü tamamlanabilir: baştan ya da kaldığı yerden.
Kaldığı yerden sürdürme, istemcinin bir başlangıç konumu bildirip komutu yinelemesiyle olur;
belirtimde bunun komutu `REST`'tir. Sunucunun sürdürmeyi destekleyip desteklemediği ise
**denetim kanalında bildirilen bir yetenektir** ve bildirimi doğrulayan hiçbir şey yoktur. Aracı
için bu kararın somut bir bedeli var: sürdürülebiliyorsa kesilen bir aktarım yalnız kalan
baytlara mal olur, sürdürülemiyorsa dosyanın tamamı yeniden akar. Ölçümde her aktarım tek bir
denemedir; sürdürmenin **bayt muhasebesine** etkisi ayrıca not edilmelidir, çünkü sürdürülen bir
aktarımda veri kanalında akan bayt sayısı bildirilen boyuttan küçüktür ve boyut karşılaştırmasına
bakan bir aracı başarılı bir aktarımı eksik sanır.

**Dizin dökümü.** `LIST` komutunun veri kanalında döndürdüğü dizin listesinin biçimi belirtimde
sabitlenmemiştir; insan okusun diye tasarlanmıştır. Sonuç, kursun kuralının en keskin biçimidir:
**alan vardır ama anlamı yoktur.** Aracı baytları görür, satırları bile ayırabilir, ama hangi
sütunun boyut hangisinin ad olduğuna dair güvenebileceği bir sözleşme yoktur. Belirtim bu boşluğu
sonradan makinenin okuyacağı bir döküm komutuyla — `MLSD` — kapatır; kapatma yolu yeni bir bayt
değil, **adı ve anlamı sabitlenmiş alanlardır.** Ölçümde `LIST` bu yüzden `yeniden_denenebilir`
tarafında `RETR` ile aynı sınıfta durur: ikisi de istasyonun durumunu değiştirmez.

#### Aracının Elindeki Alanlar

Aracı denetim kanalından komutu, yolu, sunucunun bildirdiği boyutu ve sürdürme desteğini okur.
Veri kanalından yalnız bir bayt sayısı okur. Bu iki gözlemi birleştirmek için elinde tek bir şey
vardır: **zaman yakınlığı.** Komuttan hemen sonra açılan bağlantıyı o komutun bağlantısı sayar.

Yakınlık bir imin yerini tutmaz. Aynı istasyona başka bir oturum da bağlanmışsa, aracının
bitişik saydığı bağlantı bir başkasının olabilir. Ölçüm bunu sayar.

Etkin ve edilgen kip arasındaki fark aracı açısından bir **yön** farkıdır: etkin kipte veri
bağlantısını sunucu açar, edilgen kipte istemci. Aracı ikisini de görür, ama gördüğü şey bir
bağlantı kurulumudur, bir komut değil. Bağlantıyı kimin açtığı ile **baytların hangi yöne
aktığı** ayrı iki şeydir ve birincisinden ikincisi çıkarılamaz. Baytların yönü yalnız denetim
kanalındaki komuttan okunur: `RETR` indirir, `STOR` yükler. Ölçümdeki `yuklemedir` kararının tek
kaynağı budur ve komut görünmez olduğunda o karar da gider — dördüncü rejimin sıfırı bu yüzden
sıfırdır.

```python
TOHUM = 20260809
KOMUTLAR = ["RETR", "RETR", "STOR", "LIST", "RETR", "STOR", "RETR"]
YOLLAR = ["/olcum/kuzey.csv", "/olcum/yamac.csv", "/ozet.csv",
          "/kayit.log", "/olcum/gunluk.csv"]
KARARLAR = ("yuklemedir", "yeniden_denenebilir", "olcum_dosyasi",
            "surdurulur", "tamamlandi")


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def aktarimlar(sayi=40, tohum=TOHUM):
    r, liste = uretec(tohum), []
    for i in range(sayi):
        komut, yol = KOMUTLAR[r(7)], YOLLAR[r(5)]
        bildirilen = 1024 * (1 + r(9))
        kesildi = r(5) == 0
        surdurulur = r(3) != 0
        liste.append({
            "no": i + 1, "komut": komut, "yol": yol,
            "bildirilen": bildirilen,
            "gelen": bildirilen - 512 if kesildi else bildirilen,
            "surdurulur": surdurulur,                    # kâhin
            "surdurme_imi": surdurulur and r(7) != 0,    # bildirim
            "komsu_oturum": r(5) == 0,                   # bitişik bağlantı başkasının
            "komsu_bayt": 1024 * (1 + r(9)),
        })
    return liste


def kahin(a):
    return {
        "yuklemedir": a["komut"] == "STOR",
        "yeniden_denenebilir": a["komut"] in ("RETR", "LIST"),
        "olcum_dosyasi": a["yol"].startswith("/olcum/"),
        "surdurulur": a["surdurulur"],
        "tamamlandi": a["gelen"] == a["bildirilen"],
    }


def ileti(a, gorunur):
    tam = {
        "komut": a["komut"], "yol": a["yol"], "konak": "istasyon.ornek",
        "surdurme_imi": a["surdurme_imi"], "bildirilen": a["bildirilen"],
        "bitisik_bayt": a["komsu_bayt"] if a["komsu_oturum"] else a["gelen"],
        "kanal_bayt": a["gelen"], "oturum_imi": a["no"],
    }
    return {k: v for k, v in tam.items() if k in gorunur}


def araci(i):
    k = {}
    if "komut" in i:
        k["yuklemedir"] = i["komut"] == "STOR"
        k["yeniden_denenebilir"] = i["komut"] in ("RETR", "LIST")
    if "yol" in i:
        k["olcum_dosyasi"] = i["yol"].startswith("/olcum/")
    if "surdurme_imi" in i:
        k["surdurulur"] = i["surdurme_imi"]
    if "bildirilen" in i:
        if "oturum_imi" in i:
            k["tamamlandi"] = i["kanal_bayt"] == i["bildirilen"]
        elif "bitisik_bayt" in i:
            k["tamamlandi"] = i["bitisik_bayt"] == i["bildirilen"]
    return k


DENETIM = ("komut", "yol", "konak", "surdurme_imi", "bildirilen")
GORUNUR = {
    "iki kanal açık": DENETIM + ("bitisik_bayt",),
    "veri zarflı": DENETIM,
    "tek kanal açık": DENETIM + ("kanal_bayt", "oturum_imi"),
    "tek kanal zarflı": ("konak",),
}


def olc(ad):
    dogru = yanlis = alinamayan = tur = 0
    dokum = {}
    for a in aktarimlar():
        gercek, verilen = kahin(a), araci(ileti(a, GORUNUR[ad]))
        eksik = False
        for karar in KARARLAR:
            if karar not in verilen:
                alinamayan += 1
                eksik = True
            elif verilen[karar] == gercek[karar]:
                dogru += 1
            else:
                yanlis += 1
                dokum[karar] = dokum.get(karar, 0) + 1
        tur += 1 if eksik else 0
    return dogru, yanlis, alinamayan, tur, dokum


print(f"{'rejim':<17s} {'doğru':>6s} {'yanlış':>7s} {'alınamayan':>11s} "
      f"{'tur':>4s}  yanlış dökümü")
for ad in GORUNUR:
    d, y, e, t, dk = olc(ad)
    print(f"{ad:<17s} {d:6d} {y:7d} {e:11d} {t:4d}  {dk}")

a = aktarimlar()
print()
print(f"aktarım {len(a)}, karar {len(a) * len(KARARLAR)}, "
      f"yarıda kesilen {sum(x['gelen'] != x['bildirilen'] for x in a)}, "
      f"imsiz kalan {sum(x['surdurulur'] and not x['surdurme_imi'] for x in a)}, "
      f"komşu oturum {sum(x['komsu_oturum'] for x in a)}")
```

```
rejim              doğru  yanlış  alınamayan  tur  yanlış dökümü
iki kanal açık       192       8           0    0  {'tamamlandi': 3, 'surdurulur': 5}
veri zarflı          155       5          40   40  {'surdurulur': 5}
tek kanal açık       195       5           0    0  {'surdurulur': 5}
tek kanal zarflı       0       0         200   40  {}

aktarım 40, karar 200, yarıda kesilen 7, imsiz kalan 5, komşu oturum 6
```

#### Bölünmenin Bedeli Gizlemenin Bedeli Değildir

İlk satır ile üçüncü satır **aynı miktarda alanı** görüyor: aynı komut, aynı yol, aynı bildirilen
boyut, aynı bayt sayısı. Hiçbir şey gizlenmemiş. Yine de iki kanallı rejim 192 doğru karar
alırken tek kanallı rejim 195 alıyor. Aradaki **3 karar** yalnız ve yalnız **bölünmeden** geliyor:
tek kanalda baytlar çerçevelenip oturum imi taşıdığı için eşleştirme kurulum gereği doğrudur,
iki kanalda ise aracı komşu oturumun bağlantısını bu komutun bağlantısı sanmıştır. 3/200 = 0,015;
kümenin çözünürlüğünün üç katı, dolayısıyla ölçülmüş bir farktır.

Sayının okunmasında bir incelik var. Komşu oturum **6 aktarımda** araya giriyor ama yanlış karar
yalnız **3** tanesinde doğuyor. Kalan üçünde aktarımın kendisi zaten yarıda kesilmiştir: aracı
"tamamlanmadı" der, kâhin de "tamamlanmadı" der, karar doğru çıkar. **Aracı doğru cevabı yanlış
gerekçeyle vermiştir.** Ölçüm bunu doğru sayar, çünkü ölçtüğümüz şey karardır; ama tasarım
açısından bu bir güvence değil, iki hatanın birbirini örtmesidir.

İkinci satır bambaşka bir şey söylüyor. Veri kanalı zarfa alındığında aracı bayt sayısını hiç
göremez, `tamamlandi` kararını **hiç alamaz** ve 40 aktarımın kırkında da sunucuya bir tur daha
borçlanır. Burada bir yanlış anlaşılma kapatılmalı: zarflı bir bağlantıda tel üzerinde yine bir
bayt sayısı vardır, ama o sayı dosyanın boyutu değildir — zarfın kayıt başlıkları ve dolgusu da
o toplamın içindedir. Aracının elindeki sayı ile denetim kanalındaki bildirilen boyut **aynı
birimde bile değildir**; bu yüzden karar yanlış çıkmaz, hiç çıkmaz. Karşılığında **3 yanlış karar ortadan kalkar**: kalan 5 yanlışın hepsi başka bir
kaynaktan gelir. Bu, kursun üçüncü iddiasının bu derse düşen biçimidir — **az karar, ama daha az
yanlış.** Zarf bir kusur değil bir seçimdir ve seçimin adı şudur: tur borcu ödenir, yanlış
eşleştirme satın alınmaz.

Kalan 5 yanlışın kaynağı ise ne bölünmedir ne zarf. Beş aktarımda sunucu sürdürmeyi gerçekten
destekler ama denetim kanalında **bunu bildirmemiştir**. Aracı bildirime bakar, "sürdürülemez"
der ve yanılır. Bu, kursun her dersinde tekrarlanan dördüncü okumadır: **iletide yazanı doğrulayan
hiçbir şey yoktur.** Alan eksikse aracı bilmez; yanlışsa yanlış bilir. Bu beş yanlış dört rejimin
üçünde de aynı beştir — bölmek de gizlemek de onu ne artırır ne azaltır.

#### Güvensiz Seçenek Neden Güvensiz

Temel belirtim, denetim kanalını da veri kanalını da **hiçbir zarf olmadan** taşır. Sonucu şudur:
yol boyunca duran her aygıt, oturum açarken geçen kimlik bilgisini, istenen dosya yollarını,
bildirilen boyutları ve dosyanın kendi baytlarını okuyabilir. Bu bir kusur değil, belirtimin
yazıldığı varsayımdır — yolun kendisinin güvenilir olduğu bir ağ varsayılmıştı. Varsayım
tutmadığında protokolde onu kapatacak bir alan yoktur.

Bölünmenin buraya eklediği ikinci bir nokta var: **iki kanal ayrı ayrı korunur.** Denetim kanalı
zarfa alınıp veri kanalı açık bırakılan bir kurulumda istemci oturumunu korunmuş sanır, oysa
dosyanın baytları korunmasızdır. Ölçümün ikinci satırı bunun aynadaki görüntüsüdür: orada veri
kanalı zarflıdır ve denetim kanalı açıktır; aracı yolu ve boyutu okumaya devam eder. İki kanallı
tasarımda "korunuyor mu" sorusunun **tek bir yanıtı yoktur**, kanal başına bir yanıtı vardır.

Üçüncü nokta yapısaldır. Veri kanalının adresi denetim kanalının gövdesinde pazarlık edildiği
için, adres dönüştüren bir aracının o gövdeyi **okuyup yeniden yazması** gerekir. Denetim kanalı
zarfa alındığında bunu yapamaz; aktarım kurulamaz ya da yapılandırma bir bağlantı noktası
aralığını sürekli açık tutmak zorunda kalır. Gizlemek ile ortadaki cihazın işini yapabilmesi
burada doğrudan çelişir.

**Daraltma.** Sayılan üç yüzeyin üçünü de kapatan yön aynıdır: **kanal sayısını bire indirmek.**
Tek bağlantı zarfın içine alındığında kimlik bilgisi, yol, boyut ve dosya baytları tek bir kararla
korunur; çerçeveleme eşleştirmeyi kurulum gereği çözer, dolayısıyla üç yanlış karar hiç doğmaz;
ve gövdede adres pazarlığı olmadığı için ortadaki cihazın gövdeyi yeniden yazması gerekmez. SFTP
bu yöndedir. İki kanallı düzenin korunması gerekiyorsa daraltma, **her iki kanalın korunmasının
ayrı ayrı ve açıkça istenmesidir**; birinin zarflı olması öbürü hakkında hiçbir şey söylemez.
Ölçümün son satırı bedeli de yazıyor: tek kanal zarflı rejimde aracı 200 kararın tamamını
kaybeder. Aracının önbelleklemesi, yeniden denemesi ve boyut denetimi bu rejimde yoktur; o işler
uçlara döner.

#### Özet

- Klasik dosya aktarımı bir işi iki bağlantıya böler: denetim kanalı komutları ve yanıt kodlarını,
  veri kanalı yalnız baytları taşır; baytların üzerinde hangi komuta ait olduğunu söyleyen alan
  yoktur.
- 40 aktarımlık 200 kararlık kümede iki kanallı açık rejim 192 doğru, tek kanallı açık rejim 195
  doğru karar veriyor; aradaki 3 karar **yalnız bölünmeden** gelir, gizlemeden değil.
- Komşu oturum 6 aktarımda araya giriyor ama yanlış karar 3'te doğuyor; kalan üçünde aktarım
  zaten yarıda kesildiği için aracı doğru cevabı yanlış gerekçeyle verir.
- Veri kanalı zarfa alındığında `tamamlandi` kararı 40 kez alınamaz ve 40 tur borcu doğar; buna
  karşılık 3 yanlış karar ortadan kalkar — az karar, ama daha az yanlış.
- Beş yanlış karar üç rejimde de aynıdır ve kaynağı sunucunun sürdürme desteğini bildirmemesidir;
  bölmek de gizlemek de bildirimin eksikliğini onarmaz.
- Zarfsız temel belirtimde kimlik bilgisi, yol, boyut ve dosya baytları yol boyunca okunabilir;
  iki kanal ayrı ayrı korunduğu için "korunuyor mu" sorusunun kanal başına bir yanıtı vardır ve
  daraltma kanal sayısını bire indirmektir.

#### Sonraki Adım

Bu derste ayrılan şey iki **kanaldı**: birinde ne istendiği yazıyordu, öbüründe ne geldiği. Aracı
ikisini eşleştiremediğinde bir kararı yanlış aldı. Sonraki derste ayrılan şey iki **ad** olacak.
Bir postanın nereye teslim edileceğini söyleyen alanlar ile kullanıcıya kimden geldiği gösterilen
alanlar aynı iletide durur, birbirini doğrulamaz ve teslimi yönlendiren **zarfa bakılır**. Aracının
yönlendirme için okuduğu ad ile insanın ekranda gördüğü ad ayrı iki alandan gelir; sonraki ders bu
ayrımın neden var olduğunu ve hangi denetimin onu kapattığını ölçer.

### E-posta Protokolleri

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/e-posta-protokolleri

Gönderim ile erişimin ayrımı ve postanın iki adı: teslimi yönlendiren posta zarfı ile kullanıcıya gösterilen başlık ayrı alanlardır, birbirini doğrulamaz ve aracı zarfa bakar.

Önceki ders bir işi iki **kanala** bölen bir protokolü ölçtü: komutlar birinde, baytlar öbüründe
ve aracı ikisini eşleştiremediğinde bir kararı yanlış aldı. Kayıp gizlemekten değil bölünmeden
geliyordu.

Posta bir adım ileri gider. Burada bölünen şey kanal değil **ad**dır. Bir postanın nereye teslim
edileceğini söyleyen alanlar ile kullanıcıya kimden geldiği gösterilen alanlar aynı iletinin
içinde, ayrı iki katmanda durur. Teslimi yönlendiren katman **posta zarfıdır** ve aracı ona bakar.
Kullanıcının ekranda gördüğü ad ise başlıktan gelir ve teslime hiç karışmaz. İkisi birbirini
doğrulamaz. Bu dersin sorusu, bu ayrımın **neden** var olduğu ve hangi denetimin onu kapattığıdır.

- **DG46.** Kurgu M04/K03'ün ortak kurgusudur: `istasyon.ornek` ölçüm istasyonu uyarı postaları
  üretir, `ornek.test` ve `liste.ornek` alan adları alıcı ve liste tarafını temsil eder, arada
  uygulamayı bilmeyen bir aracı durur.
- **DG47.** **Posta zarfı ile taşıma zarfı ayrı iki kavramdır.** Taşıma zarfı, güvenli taşıma
  konusunda kurulan ve aracıyı kör eden katmandır. Posta zarfı ise protokolün kendi alanlarıdır;
  hiçbir şeyi gizlemez, yalnız teslimi yönlendirir. Bu derste ölçülen posta zarfıdır.
- **DG48.** Ölçülen küme **40 ileti** ve ileti başına **5 karardır**: `teslim_yolu_var`,
  `gonderen_yetkili`, `adlar_hizali`, `gizli_alici_var`, `gosterilen_ad_dogrulanmis`. Toplam
  200 karar; ölçülebilen en küçük fark $1/200 = 0{,}0050$.
- **DG49.** İletiler dört türdendir: doğrudan, vekil (istasyon adına başka bir alanın gönderdiği),
  liste ve iletilen. **Dördü de meşrudur**; kurguda hiçbir sahte ileti yoktur. Ölçünün amacı
  sahteciliği saymak değil, **meşru çeşitliliğin denetimlere ne yaptığını** saymaktır.
- **DG50.** Alan adı politikası yayınlamak alanın seçimidir: `istasyon.ornek` ve `liste.ornek`
  yayınlar, `ornek.test` yayınlamaz. Kâhin — gerçeğin kendisi — kurguyu biz ürettiğimiz için
  bilinir ve rejimden rejime değişmez.

#### Gönderim ve Erişim

Posta iki ayrı protokol ailesiyle taşınır ve ikisi zıt yönlere bakar.

**Gönderim (submission)** iletiyi istemciden alıp alıcının posta deposuna kadar **iten** yoldur.
SMTP bu yolun belirtimidir; aynı belirtim hem istemciden ilk konağa gönderimde hem konaklar
arasında aktarımda kullanılır. **Erişim (retrieval)** ise depoya ulaşmış iletiyi kullanıcının
**çektiği** yoldur; IMAP ile POP3 bu yönün belirtimleridir. IMAP iletiyi sunucuda tutar ve
kullanıcı klasör yapısıyla birlikte çok yerden aynı görünümü okur; POP3 iletiyi indirip depodan
düşürme üzerine kuruludur.

Ayrımın bu ders için sonucu şudur: **aracı yalnız gönderim yolunda durur.** Erişim yolunda
kullanıcının kendi deposuyla arasında bir karar verme yeri yoktur. Gönderim sırasında alınmayan
bir karar, erişim sırasında geri alınamaz — ileti çoktan teslim edilmiştir. Turun borcu burada
bir posta kutusuna yazılmış olur.

İki erişim belirtiminin tur davranışı da ayrışır. POP3'te durum istemcidedir: oturum içindeki
ileti numaraları o oturuma özgüdür ve iletiler indirilip depodan düşürüldüğünde ikinci bir
istemci aynı kutuyu göremez. IMAP'te durum sunucudadır; her iletinin oturumdan oturuma değişmeyen
bir kalıcı numarası ve okundu, yanıtlandı, silindi gibi imleri vardır, bu yüzden birden çok
istemci aynı görünümü paylaşır. Bedeli şudur: paylaşılan durumun her sorgusu bir gidiş–dönüştür.
Önceki üç dersin yoklama sorusu erişim tarafında da aynen çıkar ve IMAP'in yanıtı aynı ailedendir
— `IDLE` komutu bağlantıyı açık tutar, sunucu değişikliği kendisi bildirir ve istemci boşuna tur
atmaz. Kısa yoklamanın karar başına tur maliyeti burada da geçerlidir.

#### Posta Zarfı ile Başlık

Gönderim konuşmasının iki katmanı aşağıda yan yana duruyor.

```text
gönderim konuşması — istemci ile teslim eden konak arasında

  ->  EHLO istasyon.ornek
  <-  250 merhaba
  ->  MAIL FROM:<uyari@istasyon.ornek>      posta zarfı: dönüş yolu
  <-  250 gönderen kabul edildi
  ->  RCPT TO:<nobet@ornek.test>            posta zarfı: teslim adresi
  <-  250 alıcı kabul edildi
  ->  RCPT TO:<arsiv@ornek.test>            posta zarfı: ikinci alıcı
  <-  250 alıcı kabul edildi
  ->  DATA
  <-  354 iletiyi gönderin, sonunda tek nokta

      From: Kuzey Yamac Istasyonu <uyari@istasyon.ornek>   başlık
      To: Nobet <nobet@ornek.test>                         başlık
      Subject: eşik aşıldı                                 başlık
      Date: ...

      Ölçüm eşiği aştı.
      .
  <-  250 ileti kuyruğa alındı
```

Bu döküm çalıştırılmadı; öğretilen biçimdir. Üç nokta okunmalıdır.

Birincisi, teslim **yalnız** `RCPT TO` satırlarına bakılarak yapılır. `To:` başlığı teslime hiç
karışmaz; onu okumayan bir teslim yine de doğru çalışır. İkincisi, yukarıdaki iletide iki zarf
alıcısı var ama başlıkta bir tane yazıyor — `arsiv@ornek.test` başlığa hiç girmemiştir. **Gizli
kopya bir başlık alanı değildir, bir zarf alıcısıdır**; başlıkta ona karşılık gelen bir satır
yoktur ve olmaması gerekir. Üçüncüsü, `MAIL FROM` ile `From:` ayrı iki adrestir. Birincisi
teslim edilemeyen iletinin geri döneceği yerdir, ikincisi kullanıcıya gösterilecek addır. Bir
liste iletisinde dönüş yolu listeye, gösterilen ad özgün yazara aittir; ikisinin farklı olması
protokolün kusuru değil, tasarımıdır.

Bir dördüncü nokta bu kursta ilk kez ortaya çıkıyor: **aracı burada yalnız okumaz, yazar da.**
İletiyi devralan her konak başlığın en üstüne bir `Received:` satırı ekler ve satırlar birikerek
iletinin geçtiği yolu ters sırada kaydeder. Bu, kurs boyunca aracının iletiye kendi bildirimini
koyduğu tek yerdir. Kaydın bir sınırı var ve ders için önemli: `Received:` satırları
**iletiyi taşıyan konakların kendi beyanıdır** ve alt sıradaki satırları hiçbir şey doğrulamaz.
Bir izin var olması, izin doğru olmasını gerektirmez — kursun dördüncü okuması burada da aynen
geçerlidir.

#### Ölçüm

Aracı üç görünürlük düzeyinde ölçülüyor: yalnız posta zarfını görürken, başlıkları da okurken ve
başlığı imzalayan anahtarı da almışken.

```python
TOHUM = 20260809
TURLER = ["dogrudan", "dogrudan", "dogrudan", "vekil",
          "liste", "iletilen", "dogrudan"]
ZARF_ALANI = {"dogrudan": "istasyon.ornek", "vekil": "ornek.test",
              "liste": "liste.ornek", "iletilen": "ornek.test"}
POLITIKA = {"istasyon.ornek": True, "liste.ornek": True, "ornek.test": False}
KARARLAR = ("teslim_yolu_var", "gonderen_yetkili", "adlar_hizali",
            "gizli_alici_var", "gosterilen_ad_dogrulanmis")


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def iletiler(sayi=40, tohum=TOHUM):
    r, liste = uretec(tohum), []
    for i in range(sayi):
        tur = TURLER[r(7)]
        zarf_alici = 1 + r(5)
        liste.append({
            "no": i + 1, "tur": tur,
            "zarf_alani": ZARF_ALANI[tur],
            "baslik_alani": "istasyon.ornek",
            "zarf_alici": zarf_alici,
            "baslik_alici": zarf_alici - 1 if r(5) == 0 else zarf_alici,
            "iletildi": tur in ("liste", "iletilen"),
            "kaynak_izinli": r(9) != 0,     # kâhin: çıkış konağı gerçekten yetkili
            "imza_var": r(7) != 0,
            "degistirildi": r(9) == 0,      # kâhin: ileti yolda değiştirildi
            "yol_var": r(11) != 0,
        })
    return liste


def kahin(m):
    return {
        "teslim_yolu_var": m["yol_var"],
        "gonderen_yetkili": m["kaynak_izinli"],
        "adlar_hizali": m["zarf_alani"] == m["baslik_alani"],
        "gizli_alici_var": m["zarf_alici"] > m["baslik_alici"],
        "gosterilen_ad_dogrulanmis": m["imza_var"] and not m["degistirildi"],
    }


def ileti(m, gorunur):
    tam = {
        "zarf_alani": m["zarf_alani"], "zarf_alici": m["zarf_alici"],
        "yol_var": m["yol_var"],
        "teslim_eden_izinli": m["kaynak_izinli"] and not m["iletildi"],
        "baslik_alani": m["baslik_alani"], "baslik_alici": m["baslik_alici"],
        "imza_var": m["imza_var"],
        "imza_saglam": m["imza_var"] and not m["degistirildi"],
    }
    return {k: v for k, v in tam.items() if k in gorunur}


def araci(i):
    k = {}
    if "yol_var" in i:
        k["teslim_yolu_var"] = i["yol_var"]
    if "zarf_alani" in i and POLITIKA[i["zarf_alani"]]:
        k["gonderen_yetkili"] = i["teslim_eden_izinli"]
    if "zarf_alani" in i and "baslik_alani" in i:
        k["adlar_hizali"] = i["zarf_alani"] == i["baslik_alani"]
    if "zarf_alici" in i and "baslik_alici" in i:
        k["gizli_alici_var"] = i["zarf_alici"] > i["baslik_alici"]
    if "imza_saglam" in i:
        k["gosterilen_ad_dogrulanmis"] = i["imza_saglam"]
    return k


ZARF = ("zarf_alani", "zarf_alici", "yol_var", "teslim_eden_izinli")
BASLIK = ("baslik_alani", "baslik_alici", "imza_var")
GORUNUR = {
    "zarf": ZARF,
    "zarf+başlık": ZARF + BASLIK,
    "zarf+başlık+anahtar": ZARF + BASLIK + ("imza_saglam",),
}


def olc(ad):
    dogru = yanlis = alinamayan = tur = 0
    dokum = {}
    for m in iletiler():
        gercek, verilen = kahin(m), araci(ileti(m, GORUNUR[ad]))
        eksik = False
        for karar in KARARLAR:
            if karar not in verilen:
                alinamayan += 1
                eksik = True
            elif verilen[karar] == gercek[karar]:
                dogru += 1
            else:
                yanlis += 1
                dokum[karar] = dokum.get(karar, 0) + 1
        tur += 1 if eksik else 0
    return dogru, yanlis, alinamayan, tur, dokum


print(f"{'görünürlük':<20s} {'doğru':>6s} {'yanlış':>7s} {'alınamayan':>11s} "
      f"{'tur':>4s}  yanlış dökümü")
for ad in GORUNUR:
    d, y, e, t, dk = olc(ad)
    print(f"{ad:<20s} {d:6d} {y:7d} {e:11d} {t:4d}  {dk}")

m = iletiler()
hizasiz = [x for x in m if x["zarf_alani"] != x["baslik_alani"]]
print()
print(f"ileti {len(m)}, karar {len(m) * len(KARARLAR)}, hizasız {len(hizasiz)}, "
      f"hizasızların hepsi meşru "
      f"{all(x['tur'] in ('vekil', 'liste', 'iletilen') for x in hizasiz)}")
print(f"iletilen {sum(x['iletildi'] for x in m)}, "
      f"politikası yayınlı alandan gelen {sum(POLITIKA[x['zarf_alani']] for x in m)}, "
      f"gizli alıcı taşıyan {sum(x['zarf_alici'] > x['baslik_alici'] for x in m)}")
saglam = sum(x["imza_var"] and not x["degistirildi"] for x in hizasiz)
print(f"hizasız olup imzası sağlam {saglam}, "
      f"imzasız {sum(not x['imza_var'] for x in hizasiz)}, "
      f"yolda değişmiş {sum(x['imza_var'] and x['degistirildi'] for x in hizasiz)}")
```

```
görünürlük            doğru  yanlış  alınamayan  tur  yanlış dökümü
zarf                     60       7         133   40  {'gonderen_yetkili': 7}
zarf+başlık             140       7          53   40  {'gonderen_yetkili': 7}
zarf+başlık+anahtar     180       7          13   13  {'gonderen_yetkili': 7}

ileti 40, karar 200, hizasız 20, hizasızların hepsi meşru True
iletilen 14, politikası yayınlı alandan gelen 27, gizli alıcı taşıyan 8
hizasız olup imzası sağlam 17, imzasız 2, yolda değişmiş 1
```

#### Sayıların Okunması

**Zarf teslimi yönetir, başlık hakkında hiçbir şey söylemez.** Yalnız posta zarfını gören aracı
200 kararın 60'ını alıyor; kalan 133'ünü hiç alamıyor ve 40 iletinin kırkında da bir tur
borçlanıyor. Aldığı kararlar teslimle ilgili olanlardır — yol var mı, gönderen yetkili mi.
Kullanıcının ekranda ne göreceği bu görünürlükten **hiç** çıkarılamaz. Ayrımın en temiz kanıtı
budur: teslim için gereken bilgi ile gösterim için gereken bilgi ayrı kümelerdir.

**Başlığı okumak 80 karar kazandırır ve tek bir tur eklemez.** Başlıklar aynı bağlantıda,
`DATA`'dan sonra zaten akıyor; aracı onları okuduğunda doğru karar 60'tan 140'a çıkıyor. Bu,
kursun ikinci iddiasının tersine bakan yüzüdür: alan eklemek karar kazandırmaz, ama **zaten
taşınan bir alanı okumak** kazandırabilir. Kazandıran şey baytın kendisi değil, **adı ve anlamı
sabitlenmiş** bir alan olmasıdır.

**Hizasızlık tek başına hiçbir şey söylemez.** 40 iletinin 20'sinde zarf alanı ile başlık alanı
farklıdır ve kurguda **yirmisi de meşrudur**: vekil gönderim, liste ve iletilen posta. Yalnız
hizasızlığa bakarak eleyen bir kural bu kümenin **yarısını** eler. Aracı `adlar_hizali` kararını
her seferinde doğru veriyor — ama doğru cevabını verdiği soru, cevabı aranan soru değildir.

**Yol denetimi iletmeyle bozulur.** Yedi karar yanlış çıkıyor ve yedisi de aynı kaynaktan:
yol tabanlı denetim, iletiyi **teslim eden konağa** bakar. Liste iletilerinde teslim eden konak
listenin konağıdır, oysa ileti gerçekten yetkili bir kaynaktan çıkmıştır. Aracı "yetkili değil"
der ve yanılır. Politikasını yayınlamayan alandan gelen 13 iletide ise karar **hiç alınamaz** —
eksik bildirimin bu derste ödettiği bedel yanlış değil, boşluktur.

**İmza turu bir kez ödenir.** Başlığı kapsayan imzanın doğrulanması bir anahtar alma turu ister;
bu turdan sonra aracı 180 doğru karara çıkıyor ve tur borçlusu ileti sayısı 40'tan **13'e**
düşüyor. Anahtar ileti başına değil **alan adı başına** alınır: bu kümede tek bir başlık alanı
olduğu için tur bir kez ödenir ve kırk iletinin tamamına yeter. Kalan 13, politikası yayınlanmamış
alandan gelenlerdir; onların borcu imzayla değil, yayınla kapanır.

#### Ayrım Neden Var, Hangi Denetim Onu Kapatır

Zarf ile başlığın ayrı olması sonradan eklenmiş bir gevşeklik değil, teslimin çalışması için
gereken bir ayrımdır. Teslim edilemeyen bir iletinin nereye döneceği, gösterilecek addan bağımsız
olmalıdır; yoksa bir liste iletisinin geri dönüşü özgün yazarın kutusuna düşer. Bir liste ya da
iletme, alıcı kümesini değiştirmeden iletiyi başka bir yola sokabilmelidir. Gizli kopya, başlığa
yazılmadan teslim edilebilmelidir. Üçü de zarfın başlıktan ayrı olmasını gerektirir.

Ayrımın bedeli, **başlıkta yazan adı hiçbir şeyin doğrulamamasıdır.** Ölçümdeki 20 hizasız ileti
bunun meşru yüzüdür; aynı yapı, gösterilen adın taşıdığı güveni protokolün kendisinden almaz.
Kapatan denetim üç parçadır ve üçü ayrı sorulara yanıt verir:

- **Yayınlanmış gönderen politikası (SPF)** zarf alanının hangi konaklardan posta çıkacağını
  bildirir. Yol tabanlıdır, bu yüzden iletmeyle bozulur — ölçümdeki 7 yanlışın tamamı buradan
  gelir. Yayınlanmamışsa karar alınamaz: 13 ileti.
- **Başlığı kapsayan imza (DKIM)** gösterilen adı imzalayan alanla bağlar. İletmeye dayanır,
  çünkü yola değil içeriğe bakar; ölçümde 20 hizasız iletinin **17'si** sağlam imza taşıyor.
  İçerik yolda değiştirilirse bozulur: 1 ileti. Bedeli bir anahtar alma turudur.
- **Hizalama ve raporlama politikası (DMARC)** ilk ikisinin sonucunu **başlık alanıyla** hizalar
  ve alan sahibinin ne yapılmasını istediğini bildirir. Kendi başına bir kanıt üretmez; iki
  denetimin çıktısını tek bir karara bağlar.

**Daraltma.** Sayılan yüzeylerin her birinin kapanma yönü aynıdır: **kararı yoldan içeriğe
taşımak ve sonucu başlık alanıyla hizalamak.** Yol tabanlı denetim tek başına iletmeyi
yanlışlar; içerik tabanlı imza tek başına gösterilen adla hizalanmayabilir; hizalama politikası
tek başına doğrulayacak bir şey üretmez. Üçü birlikte, aracının **gidiş–dönüşsüz** aldığı
kararların sayısını değil **doğruluğunu** artırır ve bunun karşılığında alan adı başına bir tur
ister. Bu dersin ölçüsü de zaten budur: aracı hizasızlığı zaten görüyordu, eksik olan görmek
değil, gördüğünü bağlayacak bir bildirimdi.

#### Özet

- Posta iki yönlü iki aileyle taşınır: gönderim iletiyi depoya iter (SMTP), erişim depodan çeker
  (IMAP, POP3); aracı yalnız gönderim yolunda durur ve orada alınmayan karar sonradan alınamaz.
- Teslimi posta zarfı yönetir (`MAIL FROM`, `RCPT TO`), kullanıcıya gösterilen adı başlık taşır
  (`From:`, `To:`); ikisi ayrı alanlardır, birbirini doğrulamaz ve gizli kopya yalnız zarfta
  bulunur.
- Yalnız zarfı gören aracı 200 kararın 60'ını alıyor ve 40 tur borçlanıyor; başlıkları da okumak
  hiçbir tur eklemeden doğru karar sayısını 140'a çıkarıyor.
- 40 iletinin 20'sinde adlar hizasızdır ve yirmisi de meşrudur; yalnız hizasızlığa bakan bir kural
  kümenin yarısını eler — aracı doğru cevabı, sorulmayan soruya vermektedir.
- Yol tabanlı gönderen politikası iletmeyle bozulur ve 7 kararı yanlışlar, yayınlanmadığı 13
  iletide hiç karar üretmez; başlığı kapsayan imza hizasız 20 iletinin 17'sini bağlar ve bedeli
  alan adı başına tek bir turdur.

#### Sonraki Adım

Bu iki derste ölçülen şey, aracının bir iletiden **ne okuyabildiğiydi**. Okumanın da bir önkoşulu
var: iletideki zaman damgalarının aynı saati göstermesi. Sonraki ders saati eşitleyen protokolü
ölçer ve bir tur boyunca peşinden gitmediğimiz bir varsayımı açar — gidiş ile dönüş gecikmesinin
eşit olduğu varsayımını. Eşit değilse saat sapması doğrudan bu farktan doğar ve protokolün elinde
farkı görecek hiçbir alan yoktur. Aynı ders, aygıtları yoklayan ile aygıtların bildirdiği iki
yönetim modelini tur ve karar dengesiyle karşılaştırarak kursu kapatır.

### Zaman ve Yönetim Protokolleri

Source: https://academia.sh/tr/kurslar/uygulama-protokolleri/zaman-ve-yonetim-protokolleri

Saat eşitlemenin tek varsayımı ve turun bakışımsızlığı: gidiş ile dönüş gecikmesi eşit değilse saat sapması doğrudan bu farktan doğar ve protokolün elinde farkı görecek hiçbir alan yoktur; yanında yoklayan ile bildiren yönetim modelinin tur ve karar dengesi.

Önceki iki ders aracının bir iletiden ne okuyabildiğini ölçtü. Okumanın sessiz bir önkoşulu
vardı: bir önbelleğin tazelik ömrü, bir sertifikanın geçerlilik aralığı, bir postanın `Date:`
alanı — hepsi iki ucun saatinin birbirine yakın olduğunu varsayar.

Saatleri yakınlaştıran protokol, kursun ölçü ekseninin en saf örneğidir: elinde **dört zaman
damgası** vardır ve tek bir turda saat sapmasını kestirir. Kestirim tek bir varsayıma dayanır —
**gidiş ile dönüş gecikmesinin eşit olduğuna.** Bu dersin sorusu, varsayım tutmazsa ne olduğu ve
protokolün tutmadığını görüp göremediğidir.

- **DG51.** Kurgu M04/K03'ün ortak kurgusudur: `istasyon.ornek` ve bir zaman kaynağı. Gerçek
  bir zaman sunucusu adı yazılmaz, gerçek bir ağ çağrısı yapılmaz.
- **DG52.** Ölçülen küme **40 tur**dur. Kâhin kurguyu biz ürettiğimiz için bilinir: sunucu saati
  istemciden **12,500 ms** ileridedir ve gidiş yolu dönüş yolundan **4,000 ms** uzundur. Yollar
  tur başına oynar ama bakışımsızlık sistematiktir; protokol bu iki sayıyı görmez, yalnız dört
  damgayı görür.
- **DG53.** Aygıt yönetimi tarafında küme **40 aygıt** ve **20 tiklik** bir penceredir. Her
  aygıtın durumu pencere içinde bir kez değişir; ölçü, pencere sonunda yöneticinin doğru durumu
  bilip bilmediğidir.
- **DG54.** M03'ün Sistem Yönetimi kursu saat kaymasının **günlüklerdeki belirtilerini** ölçmüştü;
  o ölçüm tekrarlanmaz. Buradaki ölçü eşitlemenin **kendi hatasıdır**.

#### Dört Damga ve İki Hesap

Saat eşitlemesi tek bir alışverişle yapılır. Her uç, iletiye dokunduğu anda kendi saatini
yazar; NTP'nin taşıdığı dört damga budur.

```text
istemci                                  sunucu (zaman kaynağı)
   |                                        |
   |-- t1: istemcinin gönderme anı -------->|
   |                                        t2: sunucunun alma anı
   |                                        t3: sunucunun gönderme anı
   |<---------------------------------------|
   t4: istemcinin alma anı                  |

   t1, t4  istemcinin saatinde okunur
   t2, t3  sunucunun saatinde okunur
```

Bu döküm çalıştırılmadı; öğretilen biçimdir. İki hesap bu dört sayıdan çıkar:

$$
\hat{\theta} = \frac{(t_2 - t_1) + (t_3 - t_4)}{2}
\qquad
\delta = (t_4 - t_1) - (t_3 - t_2)
$$

Birincisi **saat sapması (clock offset)** kestirimidir, ikincisi turun toplam gecikmesi. İkinci
hesap saat sapmasından tümüyle bağımsızdır: sapma bir kez artı bir kez eksi girdiği için düşer.
Birinci hesap ise **gidiş ile dönüşün eşit olduğunu** varsayarak ikiye böler.

Varsayım tutmazsa hatanın kapalı biçimi vardır. Gidiş gecikmesi $d_g$, dönüş gecikmesi $d_d$
olsun; kestirim ile gerçek sapma arasındaki fark tam olarak şudur:

$$
\hat{\theta} - \theta = \frac{d_g - d_d}{2}
$$

Yani hata gürültüden değil, **turun bakışımsızlığından** doğar ve farkın yarısına eşittir.

#### Ölçüm: Bakışımsızlık

```python
TOHUM = 20260809
SAPMA = 0.0125          # kâhin: sunucu saati istemciden bu kadar ileride (s)
BAKISIMSIZLIK = 0.0040  # kâhin: gidiş yolu dönüş yolundan bu kadar uzun (s)


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def turlar(sayi=40, tohum=TOHUM):
    r, liste = uretec(tohum), []
    for i in range(sayi):
        ortak = 0.0150 + 0.0001 * r(9)
        gidis = ortak + BAKISIMSIZLIK / 2 + 0.00002 * r(7)
        donus = ortak - BAKISIMSIZLIK / 2 + 0.00002 * r(7)
        t1 = 100.0 + i
        t2 = t1 + gidis + SAPMA
        t3 = t2 + 0.0002
        t4 = t3 + donus - SAPMA
        liste.append((t1, t2, t3, t4))
    return liste


def kestir(d):
    t1, t2, t3, t4 = d
    return ((t2 - t1) + (t3 - t4)) / 2, (t4 - t1) - (t3 - t2)


ol = turlar()
kestirimler = [kestir(d) for d in ol]
hatalar = [s - SAPMA for s, _ in kestirimler]
enaz = min(kestirimler, key=lambda x: x[1])
print(f"tur {len(ol)}, gerçek sapma {SAPMA * 1000:.3f} ms, "
      f"bakışımsızlık {BAKISIMSIZLIK * 1000:.3f} ms")
print(f"kestirim ortalaması {sum(s for s, _ in kestirimler) / len(ol) * 1000:.4f} ms")
print(f"hata: ortalama {sum(hatalar) / len(hatalar) * 1000:.4f}, "
      f"en küçük {min(hatalar) * 1000:.4f}, en büyük {max(hatalar) * 1000:.4f} ms")
print(f"bakışımsızlığın yarısı {BAKISIMSIZLIK / 2 * 1000:.4f} ms")
print(f"en küçük gecikmeli tur: kestirim {enaz[0] * 1000:.4f} ms, "
      f"hata {(enaz[0] - SAPMA) * 1000:.4f} ms")

fark = 0.0
for t1, t2, t3, t4 in ol:
    sapma_ikiz = ((t2 - t1) + (t3 - t4)) / 2
    yari = ((t4 - t1) - (t3 - t2)) / 2
    fark = max(fark, abs(t1 + yari + sapma_ikiz - t2),
               abs(t3 + yari - sapma_ikiz - t4))
print(f"ikiz dünyanın ürettiği damgaların en büyük farkı {fark:.3e} s")
```

```
tur 40, gerçek sapma 12.500 ms, bakışımsızlık 4.000 ms
kestirim ortalaması 14.4917 ms
hata: ortalama 1.9917, en küçük 1.9400, en büyük 2.0400 ms
bakışımsızlığın yarısı 2.0000 ms
en küçük gecikmeli tur: kestirim 14.4700 ms, hata 1.9700 ms
ikiz dünyanın ürettiği damgaların en büyük farkı 2.842e-14 s
```

#### Protokolün Göremediği

Kestirimlerin ortalaması 14,4917 ms; gerçek sapma 12,500 ms; hata 1,9917 ms ve bakışımsızlığın
yarısı 2,0000 ms. En küçük hata 1,9400, en büyük hata 2,0400 — kırk turun tamamı **iki
milisaniyenin çevresinde dar bir bantta** duruyor. Hatanın rastgele olmadığının kanıtı budur:
gürültü olsaydı hatalar sıfırın iki yanına dağılırdı.

Standart iyileştirme çok tur ölçüp **en küçük gecikmeli** turu seçmektir; en az kuyruklanmış tur
en temiz ölçüm sayılır. O tur seçildiğinde hata 1,9917'den 1,9700 ms'ye iniyor. Süzme
**gürültüyü** azaltıyor, **bakışımsızlığı** azaltmıyor — çünkü bakışımsızlık gürültü değil,
yolun kendi özelliğidir.

Son satır işin özü. Aynı dört damgayı üreten **ikinci bir dünya** kuruldu: orada yol bakışımlı ve
sunucunun saati gerçekten 14,4917 ms ileride. İki dünyanın damgaları $2{,}842 \times 10^{-14}$
saniyeye kadar aynı — bu da kayan noktalı sayının kendi çözünürlüğü. Dört damga **iki farklı
gerçeği ayırt edemez**; kestirim yanlış hesaplanmış değildir, ikiz dünyada tam olarak doğrudur.

Bakışımsızlığı gösterecek bir alan olmadığı için bu bir tur borcu bile değil: kaç tur atılırsa
atılsın kapanmaz. Kapatmanın yolu protokolün dışındadır — yolun bakışımlı olduğunu başka bir
kaynaktan bilmek ya da damgaları yola girmeden önce koymak. Kursun dördüncü okuması burada en
keskin biçimini alır: **iletide yazanı doğrulayan hiçbir şey yoktur**; burada iletide yazan
doğrudur ve yine de yetmez.

#### Yoklayan ile Bildiren

Aygıt yönetimi aynı ekseni başka bir soruyla sorar: bir yönetici kırk aygıtın durumunu nasıl
öğrenir. SNMP iki modeli birden tanımlar. **Yoklayan** modelde yönetici düzenli aralıklarla
sorar ve her soru bir gidiş–dönüştür. **Bildiren** modelde aygıt durumu değişince kendisi haber
verir; onaysız biçimi bir tur bile istemez, onaylı biçimi bir onay turu ister.

```python
TOHUM = 20260809
AYGIT, PENCERE = 40, 20


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def olaylar():
    r = uretec(TOHUM + 1)
    return [{"aygit": i + 1, "tik": 1 + r(19), "dustu": r(9) == 0}
            for i in range(AYGIT)]


def yoklama(aralik):
    tur = dogru = gecikme = 0
    for o in olaylar():
        tur += PENCERE // aralik
        gorulen = ((o["tik"] - 1) // aralik + 1) * aralik
        if gorulen <= PENCERE:
            dogru += 1
            gecikme += gorulen - o["tik"]
    return tur, dogru, gecikme / max(dogru, 1)


def bildirim(onayli):
    dusen = sum(x["dustu"] for x in olaylar())
    return (AYGIT, AYGIT, 0.0) if onayli else (0, AYGIT - dusen, 0.0)


print(f"{'model':<22s} {'tur':>5s} {'doğru':>6s} {'yanlış':>7s} {'gecikme':>9s}")
for ad, (t, d, g) in (("yoklama, aralık 5", yoklama(5)),
                      ("yoklama, aralık 10", yoklama(10)),
                      ("yoklama, aralık 20", yoklama(20)),
                      ("bildirim, onaysız", bildirim(False)),
                      ("bildirim, onaylı", bildirim(True))):
    print(f"{ad:<22s} {t:5d} {d:6d} {AYGIT - d:7d} {g:6.2f} tik")

print(f"\naygıt {AYGIT}, pencere {PENCERE} tik, "
      f"yolda düşen bildirim {sum(x['dustu'] for x in olaylar())}")
```

```
model                    tur  doğru  yanlış   gecikme
yoklama, aralık 5        160     40       0   2.08 tik
yoklama, aralık 10        80     40       0   4.95 tik
yoklama, aralık 20        40     40       0  10.95 tik
bildirim, onaysız          0     34       6   0.00 tik
bildirim, onaylı          40     40       0   0.00 tik

aygıt 40, pencere 20 tik, yolda düşen bildirim 6
```

Yoklayan modelde tur ile yakalama gecikmesi doğrudan takas edilir: aralık beşten yirmiye
çıkınca tur 160'tan 40'a iner, gecikme 2,08 tikten 10,95 tike çıkar. Doğru karar sayısı üçünde de
kırktır — yoklama geç öğrenir ama yanlış öğrenmez.

Bildiren model takası kırar. Onaylı biçim **en sık yoklamanın doğruluğunu 160 yerine 40 turla ve
sıfır gecikmeyle** veriyor. Onaysız biçim tur maliyetini sıfıra indiriyor ve altı aygıtta yanlış
karar doğuruyor. Bu altı yanlışın niteliği önemli: yönetici **sessizlik ile değişmemişlik
arasında ayrım yapamaz** ve yanıldığını da bilmez. Onay turu tam olarak bu ayrımı satın alır —
kursun kuralının bu derse düşen biçimi budur: **alınamayan karar bir tur borcudur ve borç
ödenmediğinde kararın yerini bir varsayım alır.**

#### Özet

- Saat eşitlemesi dört zaman damgasından iki hesap çıkarır: gecikme hesabı saat sapmasından
  bağımsızdır, sapma kestirimi ise gidiş ile dönüşün eşit olduğunu varsayar.
- Varsayım tutmazsa hata rastgele değildir: 40 turda gerçek sapma 12,500 ms iken kestirimlerin
  ortalaması 14,4917 ms, hata 1,9917 ms — bakışımsızlığın tam yarısı. En küçük gecikmeli turu
  seçmek hatayı ancak 1,9700 ms'ye indirir; süzme gürültüyü alır, bakışımsızlığı almaz.
- Aynı dört damgayı üreten bakışımlı bir ikiz dünya kurulabiliyor; damgalar kayan nokta
  çözünürlüğüne kadar aynı, dolayısıyla protokolün bakışımsızlığı görecek alanı yok ve bu eksik
  tur atarak kapanmıyor.
- Yönetimde yoklayan model turu gecikmeyle takas eder (160 tur ve 2,08 tik, 40 tur ve 10,95
  tik); onaylı bildiren model aynı doğruluğu 40 turla ve sıfır gecikmeyle verir.
- Onaysız bildirimde tur sıfırdır ama altı aygıtta karar yanlış çıkar; yönetici sessizlik ile
  değişmemişlik arasında ayrım yapamaz ve onay turu tam olarak bu ayrımı satın alır.

#### Kurs Kapanışı

| ders | ölçülen karar | gidiş–dönüşsüz alınan | yanlış ya da alınamayan |
|---|---|---|---|
| HTTP 01 · Yöntemler | yöntemden `yinelenebilir`, `saklanabilir` | 195/200 doğru, 0 tur | 5 yanlış: 2 eksik im, 3 doğrulayıcı |
| HTTP 02 · Durum kodları | ilk basamaktan saklama ve yeniden deneme | satır rejimi 309 doğru, 4004 bayt | 11 yanlış; gövdenin 6 kararı 673,3 bayt/karar |
| HTTP 03 · Başlıklar ve pazarlık | çözülen alan ile taşınan alanın ayrımı | 195 doğru; tam imle 13/13 doğru kopya | 5 yanlış; imsiz 9 yanlış kopya; 2960 bayt sıfır karar |
| HTTP 04 · Çerezler | kapsam kuralı ve saklanan yanıtın alıcısı | 195 doğru; kapsamlar 11, 24, 40 ileti taşır | 2 yanlış; im zarfta 80 karar uçlara, 40 tur |
| HTTP 05 · Sürümler | çerçevelemenin aracıya kazandırdığı | aynı 195 doğru, 3524 yerine 427 bayt | bağlamsız aracıda 189 alınamayan, 39 tur |
| HTTP 06 · Önbellekleme | doğrulayıcının çözünürlüğü | damga rejimi 195 doğru, 12 bayattan 9'u yakalanır | 3 kopyada damga kaçar; her seferinde sormak 40 tur |
| TLS 01 · Simetrik ve asimetrik | rolün tura maliyeti | tek kalıcı bağlantıda 0,025 tur/alışveriş | her iletide kurulumda 1,000 tur |
| TLS 02 · Sertifika zinciri | zincirin yerel doğrulanması | 33/40 doğru, 0 gidiş–dönüş; zincir uzasa da 0 | 7 yanlış, tamamı iptalden; sormak 111 tur, aynı tablo |
| TLS 03 · El sıkışması | tur bütçesi ve zarfın ödettiği karar | zarfta 40 karar, hiçbiri yanlış değil | 160 alınamayan, 40 tur; bayt 3524'ten 920'ye |
| TLS 04 · Sertifika yaşam döngüsü | iptalin tur borcu olması | sormayan istemci 0 tur; ara üç seçeneğin de alışveriş başına turu 0 | sormayan 22,02/40 yanlış kabul; her alışverişte sormak 120 tur, 0 yanlış |
| DİĞER 01 · WebSocket | yükseltmenin bir rejim değişimi olması | yükseltme isteği 5 kararın 2'sini verir | tünelde 200 alınamayan, 40 tur, 0 yanlış |
| DİĞER 02 · Sunucu akışı | akışın HTTP içinde kalması | 1 karar noktası, 5 karar, 87 bayt | 195 alınamayan, 39 tur; kapsamda 73 yanlış |
| DİĞER 03 · Yoklama | karar başına tur maliyeti | dört yaklaşımda da 0,200 tur/karar | harcanan tur ile tur borcunun toplamı hepsinde 40 |
| DİĞER 04 · Dosya aktarımı | denetim ile veri kanalının eşleşmesi | tek kanal açık 195 doğru, 0 tur | iki kanalda 8 yanlış, 3'ü bölünmeden; veri zarflı 40 tur |
| DİĞER 05 · Posta | posta zarfı ile başlığın ayrılması | zarf 60, başlıkla 140, anahtarla 180 doğru | 7 yanlış iletmeden; alınamayan 133'ten 13'e |
| DİĞER 06 · Zaman ve yönetim | turun bakışımsızlığı ve yönetim modeli | onaylı bildirim 40 doğru, 40 tur, 0 gecikme | hata 1,9917 ms, tur atarak kapanmaz; onaysızda 6 yanlış |

Tablodaki her sayı, o satırın kendi dersinin ölçümünden okunmuştur; hiçbir satır ortak tanımdan
doldurulmamıştır.

Kursun tek bir cümlesi vardı ve on altı ders onu ölçtü: **iletide ne yazıyorsa aracı onu bilir;
yazmıyorsa bilmez; yanlış yazıyorsa yanlış bilir.** Ölçü hep aynı kaldı — bir protokolün sayısı
taşıdığı alan değil, alıcı tarafa fazladan bir gidiş–dönüş olmadan kaç kararı aldırdığıdır — ve
alınamayan her karar bir tur borcu olarak yazıldı.

Kapanışta üç sonuç durur. **Alan eklemek karar kazandırmaz:** bayt büyür, karar tablosu
değişmez; kazandıran şey baytın kendisi değil, adı ve anlamı sabitlenmiş bir alan olmasıdır.
**Gizlemenin bedeli bir kusur değil bir seçimdir:** taşıma zarfı kapandığında aracı kararların
çoğunu kaybeder, ama kalanların içinde yanlış yoktur. Ve **kararı doğrulayan hiçbir şey yoktur:**
protokol bildirime inanır, çünkü bildirimden başka elinde bir şey yoktur. Son ders bunun en uç
biçimini gösterdi — orada bildirim doğruydu ve yine de yetmedi, çünkü eksik olan bir alan değil
bir dünyaydı.

Bütün bu kararları alan aracı, kurs boyunca **iletiyi** okudu. Bir sonraki kurs, Anahtarlama ve
Yönlendirme, bir katman aşağı iner ve orada duran cihaza bakar: bir anahtarın çerçeveyi hangi
bacaktan çıkaracağını, bir yönlendiricinin paketi hangi komşuya vereceğini ve bu kararların hangi
tablodan okunduğunu. Ölçü de yer değiştirir: burada aracı **ne bildiğine** göre sayılıyordu, orada
cihaz **ne kadar doğru ilettiğine** göre sayılacak. Kararı alan aracıdan, paketi ileten cihaza.

