Bölüm 13: HTTP Mesajının Anatomisi
Ağlar'a “Sen”i Katmak kitabının bir bölümü: adres çubuğuna yazdığın tek bir isteğin peşinden bakırla fiberin fiziğine inip karşı taraftan geri dönen uzun bir teknik yolculuk.
Kısım 3: İstek Yazılıyor (Bölüm 13-17)
Tüm bölümler
- Giriş
- Adres Çubuğundan İsteğe
- Paket Anahtarlamalı Yol
- İsimden Adrese
- DNS Kayıtları
- Önbellek, Tazelik ve Güven
- Port Numarası ve Soket
- Bağlantısız Taşıma: UDP
- Üç Adımlı El Sıkışma
- Simetrik Şifreleme
- Açık Anahtarlı Kriptografi
- TLS El Sıkışması
- Sertifika Zinciri
- HTTP Mesajının Anatomisi
- Durum, Çerez ve Oturum
- HTTP/2'den HTTP/3'e
- İçerik Dağıtım Ağları
- Diğer Uygulama Protokolleri
- Katmanlı Mimari
- Hizmet ve Protokol
- Encapsulation ve Başlıklar
- Paket Yakalama ve Çözümleme
- Ağ Interface Kartı
- Fiziksel Adresleme
- Hata Sezme ve CRC
- Adres Çözümleme: ARP
- Ortam Erişimi: CSMA/CD
- Switch'ler
- Ağ Topolojileri
- Sanal Yerel Ağlar
- Kablosuz Erişim: 802.11
- Radyo Kanalı ve Kapasite
- Kablosuz Ağ Güvenliği
- Hücresel Ağlar
- Spektrum ve Kuşaklar
- Hareketlilik ve Aktarma
- Adres Kiralama: DHCP
- Adresin Anatomisi
- Adres Tükenmesi ve IPv6
- Router Mimarisi
- Kuyruk Yönetimi
- Cihaz Yapılandırma
- Routing Protokolleri
- Kontrol Düzlemi ve SDN
- Ağ İzleme ve Yönetimi
- Alanlar Arası Routing
- Adres Çevirisi
- Ara Kutular
- Tünelleme ve IPsec
- Firewall'lar
- Ağ Saldırıları
- Operatör ve Metro Ağları
- Bitten Sinyale
- Sembol Hızı ve Kanal Kapasitesi
- Multiplexing Teknikleri
- İletim Ortamları
- Bakır Kablo Sonlandırma
- Fiber Sonlandırma
- Fiber Optik İletim
- Bakır Erişim Ağı: DSL
- DSL'de Hız ve Mesafe
- Pasif Optik Ağlar
- Kablo Erişim Ağları
- Kablo Modem ve CMTS
- Denizaltı Kabloları
- Veri Merkezi Ağları
- Encapsulation Çözme
- Güvenilir Aktarım
- Tıkanıklık Denetimi
- Trafik Çözümleme
- Gecikme Bütçesi
- Soket Programlama
- Eşten Eşe ve Protokol Tasarımı
- Uyarlanır Video Akışı
- Gerçek Zamanlı Ses ve Görüntü
- Kablosuz Sensör Ağlarının Mimarisi
- Sensör Ağlarında Ortam Erişimi
- Sensör Ağlarında Yönlendirme ve IP
- Zaman Eşitleme ve Konum Bulma
- Sensör Ağı Uygulamaları
- Mobil Kablosuz Sensör Ağları
- Son Söz
Kanal kuruldu, karşı taraf doğrulandı. Göndereceğin şey ise şaşırtıcı derecede sade: birkaç satır düz metin.
Şimdiye kadar “istek” deyip içine bakmadım. O metin neye benziyor, hangi kelimesi neyi belirliyor?
Bir istek neye benziyor?
HTTP web’in application layer protokolü ve modeli tek cümleye sığıyor: bir taraf istiyor, öbür taraf istenen nesneyi yanıt olarak yolluyor. Bütün protokolde yalnızca iki tür mesaj bulunuyor, istek ile yanıt; üçüncü bir tür aramana gerek yok.
Mesajın kendisi düz metin olarak gidiyor. HTTP/1.1’de her satır ASCII, yani gözünle okuyabilirsin. Karşı tarafın kim olduğu ad doğrulamasıyla çözülmüştü; o kanal bu metni tek harfini değiştirmeden taşıyor.
RFC 9110 mesajı dört parçaya ayırıyor. Başta mesajı tanımlayan ve yönlendiren denetim verisi duruyor. Arkasından ad-değer çiftlerinden oluşan başlık tablosu geliyor. Sonra sınırsız uzunlukta olabilen içerik stream’i, en sonda da kuyruk tablosu. Bir isteğin denetim verisi ilk satırda duruyor.
O satırın biçimi sabit: metot, boşluk, hedef, boşluk, sürüm ve satırı kapatan CR LF, yani 13 ve 10 numaralı iki satır sonu baytı. Hedef çoğu zaman adresin yalnızca yolu; adres çubuğuna yazdığın adresin yolu buraya giriyor. Yetkili alanı ayrı, yolu ayrı kutulara koyan ayrıştırma tam da bunun içindi.
Başlık satırları arkadan geliyor. Nerede bittiklerini satır başında duran boş bir CR LF çifti söylüyor; ondan sonrası gövde. Tamamı şuna benziyor:
GET /kitap/bolum-12 HTTP/1.1
Host: example.com
User-Agent: kendi-istemcim/1.0
Accept: text/html
Dört satır, sonra boş bir satır. Her satırın sonunda görünmeyen iki bayt daha var. Onlar da sayıya giriyor: bu istek toplam 102 bayt. İstek satırı tek başına 30 bayt tutuyor. Aynı isteğin zorunlu parçalara indirgenmiş hâli 51 bayt: istek satırı, Host başlığı ve kapanış boş satırı.
HTTP kendi taşımasını da kurmuyor. İstemci sunucunun 80. portuna bir TCP bağlantısı açıyor, mesajlar o soketten geçiyor. HTTPS’te varsayılan port 443 oluyor. Numaranın neden sabit olduğu 6. bölümde iyi bilinen portlarda geçmişti.
Bir bağlantıya kaç isteğin sığdığı ayrı bir hesap. Kalıcı olmayan HTTP’de bir TCP bağlantısında en çok bir nesne taşınıyor. Kalıcı HTTP’de sunucu yanıtı gönderdikten sonra bağlantıyı açık bırakıyor. Bunu burada tek cümleyle bırakıyorum; kalıcı bağlantının asıl hesabı 15. bölümde tutulacak.
Bir sayfa nesnelerin toplamıydı ve her nesne ayrı bir URL ile adresleniyor. O yüzden bu metin sayfa başına bir kez değil, onlarca kez yazılıyor.
Metot ne vaat ediyor?
İstek satırının ilk kelimesi isteğin anlamını taşıyor. RFC 9110 sekiz metot tanımlıyor. Küme genişletilebilir: sonradan eklenen PATCH ya da WebDAV’ın dosya işlemleri için tanımladığı metotlar IANA’nın metot defterinde kayıtlı. Yine de yeni metot nadiren icat ediliyor, çünkü anlamı herkesçe bilinmeyen bir metodu yoldaki proxy’ler ve önbellekler tanımıyor. Metot adı büyük-küçük harfe duyarlı, standart adlar da sözleşme gereği tamamı büyük harfle yazılıyor.
| Metot | Ne istiyor | Güvenli | Yinelemeye dayanıklı |
|---|---|---|---|
| GET | kaynağın güncel gösteriminin aktarılmasını | evet | evet |
| HEAD | aynı isteğin yalnızca başlıklarını | evet | evet |
| OPTIONS | kaynağın kabul ettiği seçeneklerin bildirilmesini | evet | evet |
| TRACE | mesajın yolda ne hâle geldiğinin yansıtılmasını | evet | evet |
| PUT | hedefte duranın gövdeyle değiştirilmesini | hayır | evet |
| DELETE | kaynağın güncel gösterimlerinin kaldırılmasını | hayır | evet |
| POST | gövdenin kaynağa özgü kurallarla işlenmesini | hayır | hayır |
| CONNECT | hedefe uçtan uca bir tünel kurulmasını | hayır | hayır |
Son iki sütun sözleşme. Güvenli olmak, metodun anlamının salt okunur olduğunu söylüyor. Sunucuda hiçbir şeyin değişmeyeceğini garanti etmiyor; sunucu istediği sayacı artırabilir. Söylediği şey, istemcinin değişiklik istememiş olduğu.
Yinelemeye dayanıklı metot ise aynı isteği iki kez yollamanın tek kez yollamakla aynı sonucu vermesi demek. POST ikisinin de dışında kalıyor, çünkü anlamını sunucu belirliyor.
Zorunluluk listesi kısa. Genel amaçlı her sunucu GET ve HEAD’i desteklemek zorunda; kalan altısı isteğe bağlı. HEAD’in gövdeyi hiç istememesi ucuz bir yetenek veriyor: bir dosyanın boyutunu indirmeden öğrenebiliyorsun. POST’un altına ise çok iş giriyor: form verisi, mesaj gönderme, yeni kaynak yaratma, var olan içeriğe ekleme. Yeni bir kaynak yarattıysa sunucunun 201 dönmesi ve yeni adresi Location başlığında vermesi bekleniyor.
DerinleşmeGET ile POST arasındaki fark neden teknik değil, sözleşmesel?
Aynı veriyi iki ayrı yoldan gönderebilirsin. Biri GET: veri, hedef adresin içine, soru işaretinden sonra yazılıyor. Öbürü POST: veri gövdeye giriyor ve adres temiz kalıyor. Telde ikisi de aynı sokete yazılan düz metin ve karşı taraf ikisini de okuyabiliyor.
Aradaki fark teknik değil. Fark, o istekle birlikte neyi ilan ettiğin.
GET dediğin anda isteğin salt okunur olduğunu söylemiş oluyorsun ve bu ilanı senden başka herkes ciddiye alıyor. Tarayıcı yanıtı önbelleğe koyabiliyor. Kullanıcı adresi yer imine ekleyebiliyor, sonra oradan tekrar girebiliyor. Arama motoru bağlantıyı kendiliğinden izleyebiliyor. Bir proxy, aynı adrese gelen ikinci isteği hiç sormadan kendi kopyasıyla karşılayabiliyor. Adresin içine yazdığın her şey de sunucu kayıtlarına düz metin olarak düşüyor.
POST dediğinde bunların hepsi kapanıyor. Tarayıcı sayfayı yenilerken “formu yeniden göndereyim mi” diye soruyor, çünkü ikinci gönderimin ikinci bir sipariş anlamına gelip gelmeyeceğini bilmesinin yolu yok.
Sözleşmeyi bozmanın bedelini de sen ödemiyorsun. Silme işini GET ile yapan bir uygulama telde geçerli bir mesaj gönderiyor ve hiçbir kural çiğnenmiş görünmüyor. Sonra bir gün site haritası çıkaran bir robot bütün bağlantıları geziyor, sırayla hepsini siliyor. Robot kusurlu değil, ilan edilmiş anlama göre davrandı. Bu bir kurgu da değil: 2005’te Google’ın Web Accelerator adlı hızlandırıcısı sayfadaki bağlantıları önceden gezince, “sil” bağlantısını GET ile yapan web uygulamalarında kullanıcıların verisini tam böyle sildi.
Başlıklar neyi çözüyor?
Başlık tablosunun kuralı az. Adlar büyük-küçük harfe duyarsız, yani Host ile host aynı alan.
Host zorunlu. Tek makinede birden çok alan adına hizmet veren sunucu, hangi siteyi istediğini bununla ayırt ediyor. Aynı adres, aynı port, farklı siteler. HTTP/2 ve HTTP/3’te bu alanın yerini :authority pseudo-header’ı aldı.
Gövdesi olan bir istekte iki alan daha devreye giriyor. Content-Type gövdenin ne olduğunu söylüyor ve karakter kümesi aynı satırda parametre olarak geliyor. Content-Length ise gövdenin uzunluğunu onluk tabanda bayt sayısı olarak veriyor.
POST /yorum HTTP/1.1
Host: example.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
ad=emir&yorum=merhaba+dunyaGövdenin nerede bittiğini o sayı çiziyor. Eksik yazarsan sunucu gelmeyecek baytları beklemeye devam ediyor. Fazla yazarsan bir sonraki isteğin ilk satırını gövdenin parçası sanıyor. Boş gövdeli bir POST’ta bile alan yazılıyor ve değeri sıfır oluyor.
Mesajın sonunda bir tablo daha var, ama çoğu zaman boş kalıyor. Kuyruk, gövde bittikten sonra eklenen alanları taşıyor: stream tamamlanmadan hesaplanamayan bir özet oraya yazılıyor.
Aynı adresten farklı gösterimler istemek
Bir adres tek bir dosyayı göstermek zorunda değil. /logo adresinin arkasında hem PNG hem WebP, hem Türkçe hem İngilizce, hem sıkıştırılmış hem ham bir gösterim durabilir. İstemci hangisini istediğini istek başlıklarıyla söylüyor ve mekanizmanın adı içerik pazarlığı.
Accept: image/webp,image/png;q=0.8,*/*;q=0.5
Accept-Language: tr,en;q=0.7
Accept-Encoding: gzip, brVirgülle ayrılmış her seçeneğin yanında bir q değeri var: 0 ile 1 arasında bir kalite ağırlığı, yazılmazsa 1 sayılıyor. Yukarıdaki satır “WebP’yi tercih ederim, PNG de olur, gerekirse her şeye razıyım” demek. Sunucu listeye bakıp en iyi eşleşmeyi seçiyor ve cevabında hangisini seçtiğini Content-Type ile bildiriyor.
Bir de “hangi başlığa göre seçtim” sorusu var; cevabı Vary başlığında dönüyor ve bu bilgi önbelleğin anahtarını (cache key) doğrudan etkiliyor. Pazarlık, aynı adresin arkasında birden çok kopya doğurduğu için önbelleğe de haber verilmesi gerekiyor.
Dosyanın yalnızca bir parçasını istemek
İkinci bir mekanizma, isteği bayt aralığına daraltıyor:
Range: bytes=1048576-2097151Sunucu isteği kabul ederse 206 Partial Content dönüyor ve yalnızca istenen aralığı gönderiyor; Content-Range başlığında hangi aralığı verdiğini ve dosyanın toplam boyunu yazıyor. Desteklemiyorsa 200 ile bütün dosyayı gönderiyor; istemcinin bunu anlaması için durum koduna bakması yeterli.
Bu tek başlığın üç ayrı işi var. Kesilen indirme kaldığı bayttan sürüyor. Video akışı filmi baştan indirmek yerine parça parça çekiyor. Bir dosyayı birden çok bağlantıyla paralel indiren araçlar da her bağlantıya farklı bir aralık veriyor.
18. bölümde katmanları sayarken, ağın yedi katmanlı klasik modeli olan OSI’nin session layer’ına bakacağız ve o katmanın işini bugün kimin yaptığını soracağız. Cevabın bir parçası şimdiden elinde: kopan bir aktarımı işaretli noktadan sürdürmek, ayrı bir katman değil, tek bir istek başlığı.
Üç basamak neyi söylüyor?
Yanıtın ilk satırı durum satırı: sürüm, üç basamaklı durum kodu ve reason phrase (gerekçe cümlesi) yan yana duruyor. Kodu okumak için gövdeyi beklemek gerekmiyor: istek gidip yanıtın ilk baytları dönene kadar geçen bir RTT yetiyor.
Kod bir tam sayı ve geçerli aralık 100 ile 599 arası. Sınıfı ilk rakam belirliyor; son iki basamağın sınıflandırmada hiçbir rolü yok.
| Sınıf | Anlamı | Bu bölümde geçenler |
|---|---|---|
| 1xx | bilgi | — |
| 2xx | başarı | 200, 201 |
| 3xx | yönlendirme (redirection) | 301, 304 |
| 4xx | istemci hatası | 400, 404, 405, 410 |
| 5xx | sunucu hatası | 500, 501, 505 |
İstemci tanımadığı bir kodu, o sınıfın x00 koduymuş gibi işlemek zorunda: 471 gelirse onu 400 gibi ele alıyor. Anlamını bilmiyor ama ne yapacağını biliyor. Yeni kod eklemek bu yüzden protokolü kırmıyor.
Koddan sonraki reason phrase ise yalnızca tavsiye. Yerel karşılığıyla değiştirilebilir, hiç yazılmayabilir; protokol bundan etkilenmiyor. Kararı taşıyan şey de sayının kendisi.
Asıl bilgi de birbirine yakın duran kodların arasındaki farkta. 404, kaynağın güncel bir gösteriminin bulunamadığını söylüyor. Durumun kalıcı mı geçici mi olduğunu söylemiyor; sunucu kalıcı olduğunu biliyorsa 410 tercih ediliyor. 405 ile 501 de ayrı arızalar. 405’te metot tanınıyor ama o kaynakta izinli değil ve sunucu Allow başlığıyla izinli metotları saymak zorunda. 501’de metot hiç tanınmıyor. 500 ise sunucunun beklenmedik bir durumla karşılaşıp isteği yerine getiremediği hâl. 505 istenen HTTP sürümünün desteklenmediğini bildiriyor. 301 ise nesnenin kalıcı olarak taşındığını, yeni yerinin de Location başlığında durduğunu söylüyor.
Bir kod da hiç veri taşımadığı için var. İstemci elindeki kopyanın tarihini If-Modified-Since başlığıyla isteğe yazıyor. Kaynak o tarihten sonra değişmediyse yanıt 304 oluyor ve gövde hiç gönderilmiyor. O yanıt başlık bölümü bitince bitiyor, içerik de kuyruk da taşıyamıyor. İstek ayrıca If-None-Match ile kaynağın sürüm etiketini (ETag) taşıyorsa alıcı tarihi yok saymak zorunda. Etiket üzerinden kurulan koşul daha doğru kabul ediliyor. Koşullu isteğin amacı şu: kopya güncelse nesneyi hiç göndermemek.
Bütün bunları kendi makinende görebilirsin. 80. porta elle bir bağlantı açıp istek satırıyla Host başlığını yazman yetiyor. Sonuna iki kez satır sonu koyuyorsun. Belgeleme için ayrılmış bir adla deneyebilirsin:
printf 'GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n' \
| nc example.com 80 | head -12İlk satırda sürümü, kodu ve reason phrase’i göreceksin. Altında ad-değer çiftleri sıralı duracak, sonra boş bir satır gelecek, sonra gövde başlayacak. Gördüğün şey, tarayıcının saniyede onlarca kez yazdığı metnin aynısı.

Özet
- İsteğin biçimi sabit: metot, hedef, sürüm, arkasından ad-değer çiftleri ve satır başında duran boş bir satır.
- Metot bir vaat taşır: güvenli olan salt okunur olduğunu ilan eder, yinelemeye dayanıklı olan ikinci kez yollandığında sonucu değiştirmez.
- Başlıklar mesajı yorumlanabilir kılar: Host hangi siteyi, Content-Type gövdenin ne olduğunu, Content-Length nerede bittiğini söyler.
- Durum kodu üç basamakta karar taşır: sınıfı ilk rakam belirler, tanınmayan kod x00 gibi işlenir, reason phrase yalnızca tavsiyedir.
Metni kanaldan geçirdin, yanıtın ilk satırını okudun. Ama o mesajın hiçbir yerinde senin kim olduğunu söyleyen bir alan yok; sunucu da geçmiş istekler hakkında hiçbir bilgi tutmuyor. İsteği yazdın; peki sunucu seni daha önce gördüğünü nereden bilecek?
14. bölüme devam et: Durum, Çerez ve Oturum