Ders 01 / 16
HTTP Yöntemleri ve Anlamları
Yöntem bir bildirimdir: aradaki aracı ondan yinelenebilir ve saklanabilir kararını çıkarır, ve bildirimi doğrulayan hiçbir şey yoktur.
İçindekiler
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:
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:
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 ’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.
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.
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:
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 POSTtur. 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
yinelenebilirvesaklanabilirkararları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.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.