Ders 10 / 12
Tarayıcı Ne Yapar
Adres satırındaki metnin çözümlenmesinden ekrandaki çizime uzanan aşamalar, alt kaynak istekleri ve bağlantıların yeniden kullanımı.
İçindekiler
Önceki ders tek bir isteği ve tek bir yanıtı inceledi. Buna karşın bir sayfa açıldığında tek bir istek yapılmaz. Gelen belge başka kaynaklara başvurur ve tarayıcı onları da ister.
Bu ders tarayıcının işini sırasıyla ele alır: adres satırındaki metnin çözümlenmesinden ekrandaki çizime kadar. Tarayıcı bir görüntüleyici değil, bu aşamaları yürüten bir düzenleyicidir.
Adresin Çözümlenmesi
İlk iş, yazılan metni bölümlerine ayırmaktır. Bir adres tek bir dizgi değil, yapılı bir değerdir.
from urllib.parse import urlsplit adres = "https://ornek.test:8443/arsiv/belge?konu=ag&sayfa=2#bolum3" parca = urlsplit(adres) print("sema :", parca.scheme) print("konak :", parca.hostname) print("bag. nok. :", parca.port) print("yol :", parca.path) print("sorgu :", parca.query) print("parca :", parca.fragment)
sema : https konak : ornek.test bag. nok. : 8443 yol : /arsiv/belge sorgu : konu=ag&sayfa=2 parca : bolum3
Bölümlerin her biri farklı bir aşamada kullanılır:
| Bölüm | Kullanıldığı yer |
|---|---|
| Şema | Hangi protokolün konuşulacağı; bağlantı noktası varsayılanını da belirler |
| Konak | Alan Adı Sistemi dersindeki çözümlemeye verilen ad |
| Bağlantı noktası | Bağlantı kurulurken; yazılmazsa şemanın varsayılanı |
| Yol | İstek satırına konur, sunucuya gönderilir |
| Sorgu | İstek satırına yolla birlikte konur, sunucuya gönderilir |
| Parça | Sunucuya gönderilmez; yalnızca tarayıcıda kullanılır |
Son satır dikkat çekicidir. #bolum3 bölümü ağa hiç çıkmaz; tarayıcı belgeyi aldıktan
sonra o adı taşıyan bölüme kaydırmak için kendisi kullanır. Sunucu bu değeri göremez.
Adın büyük–küçük harf ayrımı gözetmediği Alan Adı Nedir dersinde belirtilmişti. Yol bölümü
için aynı şey geçerli değildir: /Arsiv ile /arsiv farklı iki yol olabilir ve bunu
belirleyen sunucudur.
Aşamalar
Adres çözümlendikten sonra tarayıcı şu adımları izler:
- Ad çözümleme. Konak adı bir adrese çevrilir. Alan Adı Sistemi dersindeki zincir burada işler ve önbellekler devreye girer.
- Bağlantı kurma. Hedef adres ve bağlantı noktasına bağlanılır.
- Güvenlik el sıkışması. Şema
httpsise şifreli kanal kurulur; sonraki dersin konusudur. - İstek gönderme. Önceki derste tanımlanan biçimde istek yazılır.
- Yanıt alma ve yorumlama. Durum kodu ve
Content-Typebaşlığına göre gövdenin ne olduğu belirlenir. - Belgeyi ayrıştırma. İçerik bir belgeyse yapısı çıkarılır.
- Alt kaynakları isteme. Belgenin başvurduğu her kaynak için 1–5 adımları yinelenir.
- Yerleşim ve çizim. Öğelerin ekrandaki yeri ve görünümü hesaplanır, çizilir.
Adımların bir bölümü örtüşür. Tarayıcı belgenin tamamını beklemeden ayrıştırmaya başlar ve gördüğü ilk alt kaynak başvurusunda isteği yollar.
Alt Kaynak İstekleri
Yedinci adım, açılan bir sayfanın neden tek bir isteğe indirgenemeyeceğini açıklar. Aşağıdaki sunucu üç kaynak sunar; kök belge diğer ikisine başvurur.
from http.server import BaseHTTPRequestHandler, HTTPServer SAYFALAR: dict[str, tuple[str, bytes]] = { "/": ("text/html; charset=utf-8", b"<!doctype html>\n<html lang=\"tr\">\n" b"<head><link rel=\"stylesheet\" href=\"/bicem.css\"></head>\n" b"<body><h1>Ornek Sayfa</h1><img src=\"/logo.svg\" alt=\"\"></body>\n" b"</html>\n"), "/bicem.css": ("text/css; charset=utf-8", b"h1 { color: #333; }\n"), "/logo.svg": ("image/svg+xml", b"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"8\" height=\"8\"></svg>\n"), } class Isleyici(BaseHTTPRequestHandler): protocol_version = "HTTP/1.1" def log_message(self, bicem: str, *arg: object) -> None: print(f"{self.command} {self.path} -> {arg[1]}", flush=True) def _yanitla(self, durum: int, tip: str, govde: bytes, govde_gonder: bool) -> None: self.send_response_only(durum) self.send_header("Content-Type", tip) self.send_header("Content-Length", str(len(govde))) self.end_headers() if govde_gonder: self.wfile.write(govde) def _dagit(self, govde_gonder: bool) -> None: if self.path in SAYFALAR: tip, govde = SAYFALAR[self.path] self.log_request(200) self._yanitla(200, tip, govde, govde_gonder) else: govde = b"<h1>404 Bulunamadi</h1>\n" self.log_request(404) self._yanitla(404, "text/html; charset=utf-8", govde, govde_gonder) def do_GET(self) -> None: self._dagit(govde_gonder=True) def do_HEAD(self) -> None: self._dagit(govde_gonder=False) HTTPServer(("127.0.0.1", 8080), Isleyici).serve_forever()
Bir tarayıcının yapacağı üç istek, curl ile açıkça yapılır:
curl -sv --resolve ornek.test:8080:127.0.0.1 -A "ornek-istemci" -o /dev/null \ http://ornek.test:8080/ http://ornek.test:8080/bicem.css http://ornek.test:8080/logo.svg
Sunucunun kaydı üç isteği gösterir:
GET / -> 200 GET /bicem.css -> 200 GET /logo.svg -> 200
Kullanıcı tek bir adres yazdı; sunucu üç istek gördü. Gerçek sayfalarda bu sayı çok daha yüksektir ve alt kaynaklar başka alan adlarında bulunabilir — bu durumda her yeni alan adı için ad çözümleme ve bağlantı kurma adımları yeniden yürütülür.
Bağlantıların Yeniden Kullanımı
Her istek için ayrı bir bağlantı kurmak pahalıdır. Bağlantı kurmak en az bir gidiş–dönüş süresi gerektirir; şifreli bağlantıda daha fazlası. Ağ Nedir dersinde kurulduğu üzere gecikmenin bir bölümü fiziksel olarak azaltılamaz, dolayısıyla gidiş–dönüş sayısını azaltmak doğrudan bir kazançtır.
Yukarıdaki üç isteğin bağlantı davranışı incelendiğinde durum görünür:
* Trying 127.0.0.1:8080... * Connected to ornek.test (127.0.0.1) port 8080 > GET / HTTP/1.1 < HTTP/1.1 200 OK * Connection #0 to host ornek.test left intact * Re-using existing connection with host ornek.test > GET /bicem.css HTTP/1.1 < HTTP/1.1 200 OK * Connection #0 to host ornek.test left intact * Re-using existing connection with host ornek.test > GET /logo.svg HTTP/1.1 < HTTP/1.1 200 OK * Connection #0 to host ornek.test left intact
Bir kez bağlanıldı, üç istek aynı bağlantı üzerinden geçti. Bu kalıcı bağlantı (persistent connection) davranışıdır ve HTTP/1.1 ile protokolün varsayılanı durumuna gelmiştir; önceki sürümde her istek için bağlantı kurulup kapatılıyordu.
Kalıcı bağlantının çözmediği bir sorun kalır. Aynı bağlantı üzerindeki istekler sırayla işlenir: ilk yanıt tamamlanmadan ikincisi gönderilemez. Yavaş bir yanıt, arkasındakileri bekletir. Bu davranış sıra başı engellemesi olarak adlandırılır.
HTTP/2, aynı bağlantı üzerinde birden çok isteğin iç içe geçmesine izin vererek bu soruna yanıt verir; buna çoğullama (multiplexing) denir. İstekler ve yanıtlar numaralanmış akışlara bölünür, parçalar karışık sırayla gönderilir ve alıcı tarafta ayrıştırılır. Bu fikir, Ağ Nedir dersindeki paket numaralandırma ile aynı akıl yürütmedir.
| Özellik | Ayrı bağlantı | Kalıcı bağlantı | Çoğullama |
|---|---|---|---|
| İstek başına bağlantı kurma | Her istekte | İlk istekte | İlk istekte |
| Aynı anda bekleyen istek | Bağlantı sayısı kadar | Bir | Çok |
| Yavaş yanıtın etkisi | Yalnız kendisi | Arkasındakileri bekletir | Diğerlerini bekletmez |
Bu üç davranış, protokolün sürümleri arasındaki teknik ayrımdır. Hangisinin kullanıldığı istemci ile sunucunun bağlantı kurarken anlaşmasıyla belirlenir.
Belgeden Çizime
Alınan belge metindir; ekranda görünen ise yerleşimi ve biçimi hesaplanmış öğelerdir. Arada üç iş vardır.
Ayrıştırma. Belge metni, iç içe geçmiş öğelerden oluşan bir ağaca çevrilir. Veri Yapıları kursundaki ağaç yapısı burada doğrudan karşılığını bulur: her öğe bir düğüm, iç içelik ise ata–alt ilişkisidir.
Biçim hesabı. Biçem kuralları öğelere uygulanır ve her öğenin görünüm özellikleri belirlenir.
Yerleşim ve çizim. Her öğenin ekrandaki konumu ve boyutu hesaplanır, sonra çizilir.
Alt kaynakların bu sırayı etkileme biçimi farklıdır ve bu fark gözlenebilir bir sonuç doğurur. Biçem dosyası alınmadan çizim yapmak, sayfanın önce biçimsiz görünüp sonra değişmesine yol açar; bu yüzden çizim biçem dosyası beklenerek yapılır. Betikler ise belgeyi değiştirebildikleri için ayrıştırmayı durdurabilir. Görseller ise beklenmez; geldiklerinde yerlerine konur.
Bu, alt kaynakların yalnızca sayılarının değil sıralarının da sayfanın görünme süresini belirlediği anlamına gelir.
Tarayıcı Önbelleği
Alınan kaynakların yeniden istenmemesi için tarayıcı bir önbellek tutar. Alan Adı Sistemi dersindeki yaşam süresi mantığı burada da geçerlidir, ancak denetim başlıklarla yapılır.
Sunucu bir yanıtın ne kadar süre saklanabileceğini bildirir. Süre dolduğunda tarayıcı kaynağı yeniden indirmek yerine koşullu istek yapar: elindeki kopyanın kimliğini gönderir ve “değiştiyse gönder” der. Değişmemişse sunucu gövdesiz kısa bir yanıt döndürür ve gövde ikinci kez taşınmamış olur.
Bu düzen, ilk açılış ile sonraki açılışlar arasındaki farkın kaynağıdır ve önceki dersteki
HEAD yönteminin gerekçesiyle aynı fikre dayanır: içeriği taşımadan içerik hakkında bilgi
almak.
Özet
- Adres yapılı bir değerdir; şema, konak, bağlantı noktası, yol, sorgu ve parça bölümleri farklı aşamalarda kullanılır ve parça bölümü sunucuya hiç gönderilmez.
- Tarayıcı ad çözümleme, bağlantı kurma, istek gönderme, ayrıştırma, alt kaynak isteme ve çizim adımlarını yürütür; adımlar örtüşerek ilerler.
- Tek bir adres yazılması çok sayıda isteğe yol açar; alt kaynaklar farklı alan adlarındaysa çözümleme ve bağlantı adımları yeniden yürütülür.
- Kalıcı bağlantı, bağlantı kurma maliyetini istekler arasında paylaştırır; çoğullama, aynı bağlantıdaki isteklerin birbirini beklemesini ortadan kaldırır.
- Belge bir öğe ağacına çevrilir; biçem beklenerek, betikler ayrıştırmayı durdurarak, görseller beklenmeden işlenir.
- Tarayıcı önbelleği, süresi dolan kaynaklar için koşullu istek yaparak gövdenin yeniden taşınmasını önler.
Sonraki Adım
Bu ders adresin şema bölümünü ayırdı ve https durumunda bir güvenlik el sıkışması
yapıldığını söyleyip geçti. O adım atlanamaz: alışverişin yol üzerindeki hiç kimse
tarafından okunamaması, değiştirilememesi ve karşı tarafın gerçekten iddia ettiği taraf
olması gerekir. Sonraki ders bu üç sözü ayrı ayrı tanımlayacak ve yerel olarak üretilmiş bir
sertifikayla her birinin nasıl sınandığını gösterecek.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.