hkucuk

QUIC Protokolü ve HTTP/3: Taşıma Katmanında Devrim ve Uygulama Rehberi

4 Ekim 2025 • ☕️☕️☕️☕️ 19 dk okuma • 🏷 bilgisayar, yazılım, algoritma

Yazar tarafından şu dillere çevrildi: English


QUIC, UDP üzerinde çalışan, TLS 1.3 ile entegre, çok akışlı (multiplexed) ve göç edebilir (connection migration) bir taşıma protokolüdür. HTTP/3 doğrudan QUIC üzerinde çalışır. QUIC; TCP+TLS’in el sıkışma gecikmesini azaltır, HTTP/2’nin TCP-temelli head-of-line blocking sorununu ortadan kaldırır, adres doğrulama ve anti-amplification gibi güvenlik önlemlerini çekirdek tasarımına gömer ve uzantılarla düşük gecikimli uygulamalara kapı açar. Bu makalede QUIC’in motivasyonu, çalışma ilkeleri, kabiliyetleri, HTTP/3 ile ilişkisi, yönetilebilirlik ve ölçülebilirlik boyutu, avantaj/dezavantajları, TCP/HTTP2/SCTP gibi alternatiflerle karşılaştırması ve GoLang ile pratik kod örnekleri yer almaktadır.

QUIC


1. Neden QUIC?

TCP + TLS + HTTP/2’nin bedeli:

  • El sıkışma gecikmesi: TCP üç yönlü el sıkışma + TLS el sıkışması tipik olarak 1–2 RTT gecikme ekler. QUIC, TLS 1.3’ü taşıma katına entegre ederek ilk bağlantıyı çoğu durumda tek RTT’de tamamlar; yeniden bağlantıda 0-RTT ile uygulama verisini hemen gönderebilir. 
  • HOL Blocking: HTTP/2 birden fazla isteği tek bir TCP akışında çoklar; tek bir TCP segmentindeki kayıp, o TCP akışındaki tüm HTTP/2 akışlarını bekletir. QUIC’te her uygulama akışı (stream) bağımsızdır; tek akıştaki kayıp diğerlerini durdurmaz. 
  • Orta nokta sertleşmesi (ossification): TCP’nin açık tel görüntüsü (wire image) ağ cihazlarının, yeni özellikleri zorlaştıran kestirimci davranışlar geliştirmesine yol açtı. QUIC; sürümlemenin kendisini ve sürüm-bağımsız invariyantları tanımlayarak değişime alan bırakır; ayrıca QUIC v2 neredeyse v1 ile aynı olup sadece “fiziksel görünüş” değişiklikleriyle ağdaki sertleşmeye karşı alıştırma yapar.

Operasyonel gereksinimler: Şifrelemenin varsayılan oluşu, bağlantı göçü, path doğrulaması, NAT yeniden bağlama dayanıklılığı ve genişletilebilirlik modern web ve mobil uygulamaların gereksinimlerini karşılar.

Zero RTT

2. Mimari Öz: UDP Üzerinde Güvenli, Çok Akışlı, Sürümlemeli

QUIC, UDP üzerinde çalışan bir taşıma protokolüdür; paket başlıkları ve çerçevelemeyi kendi tanımlar, kayıp tespiti ve tıkanıklık kontrolü gibi TCP’de çekirdekte olan işlevleri kullanıcı alanına taşır. Bu mimari, protokol evrimini hızlandırır (güncelleme/deploy kolaylığı) ve Connection ID (CID) kullanımı sayesinde NAT arkasında veya IP/port değişimlerinde bağlantı göçünü mümkün kılar. CID’ler, akışları uçtan uca korurken orta noktalarda yük dengelemeyi de kolaylaştırır; tel görüntüsünde yalnızca gerekli minimum sinyaller görünür, geri kalanı şifrelenir.

TLS 1.3, QUIC’in içine entegredir; el sıkışma, kimlik doğrulama ve anahtar üretimi taşıma katmanının parçası olarak gerçekleşir. İlk kurulum genellikle 1-RTT’de tamamlanır; oturum yeniden başlatmada 0-RTT ile (yeniden oynatma riskleri gözetilerek) uygulama verisine erken başlanabilir. Sunucu, anti-amplification ve Retry gibi mekanizmalarla istemci adresini doğrular; uzun ömürlü oturumlarda anahtar güncelleme akışı gizliliği güçlendirir. Uygulama protokolleri, ALPN üzerinden QUIC üzerinde hangi protokolün (örn. h3) konuşulacağını ilan eder.

Veri taşıma akış (stream) soyutlamasıyla yapılır: çift yönlü veya tek yönlü akışlar, hem akış düzeyi hem de bağlantı düzeyi akış kontrolüne tabidir. Her akış bağımsız ilerlediğinden tek bir kayıp diğer akışları durdurmaz; bu, HTTP/2’nin TCP kaynaklı head-of-line blocking sorununu ortadan kaldırır. Güvenilir aktarım gerektiren içerikler akışlarla taşınırken, QUIC DATAGRAM çerçevesi güvenilmez ama şifreli ve tıkanıklık kontrolüne tabi en iyi çaba (best-effort) iletim sağlar; medya/telemetri gibi gecikmeye duyarlı uygulamalar için uygundur.

Paket düzeyinde QUIC, Initial, Handshake, 0-RTT ve 1-RTT gibi farklı paket türleri ve bunlara karşılık gelen ayrı paket numarası uzayları kullanır; her uzayın ACK’leri ve zamanlayıcıları bağımsızdır. Bu ayrım, el sıkışma/kriptografik aşamaların uygulama verisinden izolasyonunu sağlar ve kayıp tespitini sadeleştirir. Sürüm müzakeresi, uçların ortak QUIC sürümünü ek RTT ödemeden seçmesine izin verir; QUIC v2 ise esasen v1’in işlevsel eşleniği olup tel görünüşünü farklılaştırarak orta nokta sertleşmesine karşı çeşitlilik yaratır ve gelecekteki protokol evrimine alan açar.

  • UDP taşıyıcı, QUIC taşıma: QUIC, UDP datagramlarına kendi başlık ve çerçevelemelerini koyar; kayıp tespiti ve tıkanıklık kontrolü QUIC katmanındadır. 
  • TLS 1.3 entegre: El sıkışma, kimlik doğrulama ve anahtar üretimi QUIC içinde gerçekleşir; 1-RTT (ilk kurulum), 0-RTT (yeniden başlatma) desteklenir. 
  • Akışlar (Streams): İki yönlü/tek yönlü akışlar, akış ve bağlantı düzeyi akış kontrolü ile yönetilir. Bir akıştaki yeniden iletim diğer akışları bekletmez. 
  • Paket türleri ve numara alanları: Initial, Handshake, 0-RTT ve 1-RTT paketleri; packet number space’leri ayrıdır. 
  • Sürüm Müzakeresi ve QUIC v2: Uyumlu sürüm müzakeresi ile ekstra RTT ödenmeden tarafların ortak sürümü seçmeleri mümkün; QUIC v2 “görünüş” farklılıklarıyla çalışır.

3. Bağlantı Yaşam Döngüsü, Güvenlik ve Anti-Amplification

El sıkışma, QUIC’te TLS 1.3 ile bütünleşiktir: ilk bağlantı genellikle 1-RTT’de tamamlanır; yeniden bağlanmada 0-RTT “erken veri” ile uygulama yükünü el sıkışma bitmeden göndermeye izin verir. 0-RTT yalnızca önceki bir oturumdan alınan bilet/ticket ve parametrelerle mümkündür ve yeniden oynatma (replay) riskleri nedeniyle sunucu tarafında politika kısıtlarına tabidir (ör. idempotent GET dışındaki işlemleri reddetmek, kota koymak, belirli süre pencereleri uygulamak). El sıkışma sırasında ALPN ile üst protokol (ör. h3) seçilir; Handshake tamamlandığında 1-RTT anahtarları devreye girer ve veri akışı tam güvenli moda geçer.

Adres doğrulama ve anti-amplification ilkesi, yansıma/güçlendirme saldırılarına karşı çekirdek güvenlik önlemidir: sunucu, istemcinin adresi doğrulanana kadar aldığının en fazla 3 katı bayt gönderebilir. Adres doğrulaması iki şekilde yapılır: (i) daha önce doğrulanmış bir oturumda verilmiş NEW_TOKEN kullanımıyla sıfır ek gecikme; (ii) ilk kez bağlanan istemci için Retry paketi ile token temelli doğrulama (çoğu durumda +1 RTT maliyet). Bu aşamada büyük sertifika zincirleri veya gereksiz büyük ilk yanıtlar amplifikasyon sınırına takılabileceğinden, zinciri ve ilk veri boyutlarını optimize etmek pratikte önemlidir.

Bağlantı göçü (connection migration), Connection ID (CID)’ler sayesinde IP/port değiştiğinde dahi akışların sürmesini sağlar; mobil cihazın Wi-Fi → hücresel geçişinde uygulama kesilmez. Yeni yola geçildiğinde uç, PATH_CHALLENGE / PATH_RESPONSE ile yolun ulaşılabilirliğini doğrular; genellikle yeni yol için RTT/cwnd ölçümleri sıfırlanır ve güvenli başlangıç yapılır. NAT rebinding gibi kısa süreli uç değişimleri de bu mekanizmalarla tolere edilir. Hata durumunda, karşı tarafın sessizce yok olduğu senaryolarda Stateless Reset hızlı kapanmayı mümkün kılar; idle timeout ve draining durumları bağlantı yaşam döngüsünün diğer kapanış aşamalarıdır.

Uzun ömürlü akışlarda anahtar güncelleme (key update) periyodik olarak yapılabilir: uçlar, belirli miktar veri/packet sonrası yeni 1-RTT anahtarlarına geçerek kriptografik gizliliği güçlendirir ve olası anahtar sızması etkisini sınırlar. Standartlaştırma ekosisteminde Extended Key Update gibi öneriler, güncelleme davranışını daha incelikli hâle getirerek büyük veri akışlarında performans–güvenlik dengesini korumayı amaçlar. Böylece QUIC’in bağlantı yaşam döngüsü; hızlı el sıkışma, kontrollü amplifikasyon, yol doğrulaması ve düzenli anahtar yenilemeleri üzerinden hem güçlü güvenlik hem de pratik dayanıklılık sunar.

  • El sıkışma ve 0-RTT: İlk bağlantı genellikle 1-RTT’de tamamlanır; yeniden bağlanmada 0-RTT ile uygulama verisi el sıkışma tamamlanmadan gönderilebilir. 0-RTT’nin yeniden oynatma (replay) riskleri vardır; kimlik doğrulaması sonlamadan hassas işlem yapmayın.
  • Adres doğrulama ve anti-amplification limiti: Sunucu, istemci adresi doğrulanana kadar aldığı baytın en fazla 3 katı veri gönderebilir; bu, yansıma (reflection) ve güçlendirme saldırılarını sınırlar. Gerekirse Retry paketi ile token tabanlı doğrulama ister; bu ek RTT getirebilir. 
  • Bağlantı göçü ve path doğrulaması: Bağlantı kimlikleri (CIDs) sayesinde IP/port değişse de akışlar devam eder; yeni yol PATH_CHALLENGE/RESPONSE ile doğrulanır. 
  • Anahtar güncelleme: Uzun ömürlü bağlantılar için anahtar güncellemesi ve yeni “Extended Key Update” taslakları gündemdedir.

Zero RTT

4. Kayıp Tespiti ve Tıkanıklık Kontrolü (RFC 9002)

QUIC’in kayıp tespiti, TCP’deki klasik RTO yaklaşımını modernize ederek Probe Timeout (PTO), ACK aralıkları (ACK ranges) ve gecikmeli ACK (ACK delay) kavramlarını bir araya getirir. Her paket ayrı bir packet number space (Initial/Handshake/1-RTT) içinde izlenir; böylece el sıkışma aşamasındaki belirsizlikler uygulama verisinden izole edilir. Alıcı, Selective ACK’e benzer biçimde hangi paket aralıklarını gördüğünü bildirir ve ölçtüğü ACK gecikmesini de raporlar; gönderen bu bilgiyi RTT kestirimi ve zamanlayıcılarını ayarlamak için kullanır. PTO, “zamanında yanıt gelmedi, ağ sessiz olabilir” sinyalidir; gönderene ek prob (araştırma) paketleri yollatır ve böylece gereksiz tam zaman aşımı beklemeleri azaltılır.

Kayıp kararı iki ana sezgiye dayanır: paket-eşiği (daha yeni N paket onaylandıysa eskisi muhtemelen kayıptır) ve zaman-eşiği (tahmini RTT penceresi aşıldıysa kayıp varsayılır). Bu ikili yaklaşım, hem yeniden sıralanma (reordering) hem de değişken gecikim koşullarında yanlış kayıp saptamalarını sınırlamayı hedefler. Uzun süreli kötüleşmeler için QUIC, kalıcı tıkanıklık (persistent congestion) durumunu tanımlar: belirli bir süre (PTO tabanlı bir eşik) boyunca gönderilen tüm “ACK uyandırıcı” paketler kayıp sayılırsa, tıkanıklığın ciddi olduğu kabul edilip daha agresif çakışma azaltma uygulanır.

Tıkanıklık kontrolü pluggable tasarlanmıştır; RFC 9002 algoritma seçimini dikte etmez, ancak pencereli algoritmalar için rehber metrikleri (örn. bytes-in-flight, pacing) ve başlangıç davranışlarını çizer. Pratikte başlangıç congestion window yaklaşık 10 paket mertebesindedir ve gönderenin pacing yapması (ani patlamaları önlemek için aralıklı gönderim) tavsiye edilir. Kaybın sinyali (ya da ECN işaretleri) alındığında yavaş başlatma dışına çıkılır ve congestion avoidance evresine geçilir; pencere büyümesi yavaşlatılır veya düşürülür. Bu sayede QUIC; yüksek bant genişlikli, yüksek gecikimli hatlarda dahi gereksiz dalgalanmayı sınırlarken iyi hatlarda hızla “açılabilir”.

Algoritma tarafında NewReno ve CUBIC en yaygın uygulanımlardır; BBR gibi akış-temelli yaklaşımlar da kullanıcı alanında rahatça denenebilir. Ayrıca QUIC, ağ destekliyorsa ECN sinyallerini tıkanıklık göstergesi olarak kullanabilir; bu, yalnızca kayba güvenmekten daha nazik bir tepkisellik sağlar. Uygulama açısından bakarsak: gecikmeye duyarlı medya/oyun trafiği için QUIC DATAGRAM + pacing iyi sonuç verirken; güvenilir aktarım gereken dosya/HTTP içerikleri akış (stream) üzerinde, seçilen tıkanıklık kontrolüyle tahmin edilebilir ve kararlı bir performans elde eder.

5. HTTP/3: QUIC Üzerinde HTTP

HTTP/3, HTTP semantiğini doğrudan QUIC akışlarına yerleştirir: her istek–yanıt çifti tek bir çift yönlü akış üzerinde taşınır; akışlar birbirinden bağımsız ilerlediği için tek bir kayıp ya da yeniden iletim diğer istekleri bekletmez. HTTP/3 ayrıca denetim (control) akışları kullanır: SETTINGS, GOAWAY ve benzeri çerçeveler bu tek yönlü kontrol akışlarında taşınır; istek akışlarının üzerine karışmaz. Bu yapı, HTTP/2’de TCP’nin tek bayt akışına bindirilmiş çoklamanın tetiklediği TCP kaynaklı head-of-line blocking sorununu kökten kaldırır.

Başlık sıkıştırması HTTP/3’te QPACK ile yapılır. QPACK, HTTP/2’deki HPACK’in “tek TCP akışında dinamik tablo güncellemesi → global HOL” problemine karşı tasarlanmıştır. Dinamik tablo güncellemeleri ve başlık başvuruları, ayrı encoder/decoder tek yönlü akışlarında mübadele edilir; istek akışları bu akışlardan bağımsız ilerler. Aynı zamanda “gerekli ekleme sayısı” gibi kısıtlayıcı işaretler sayesinde alıcı, henüz görmediği bir dinamik tablo girdisine takılıp bloklanmaz; pratikte yüksek kayıp ve yeniden sıralama içeren mobil ağlarda dahi başlık kod çözme akış bazında ilerler.

Önceliklendirme ve akış kontrolü, QUIC’in doğal yetenekleriyle birleşir. Uygulama; büyük gövdeli indirmeleri düşük, etkileşimli istekleri yüksek öncelikli işaretleyebilir; QUIC’in stream-level ve connection-level akış kontrolü, adil ve tahmin edilebilir kaynak kullanımını sağlar. Güvenilir veri (HEADERS/DATA) akışlar üzerinden giderken, HTTP Datagrams (H3 DATAGRAM) seçeneği, QUIC’in güvenilmez ama şifreli DATAGRAM çerçevesine bağlanır; medya, oyun telemetrisi veya tünelleme (ör. MASQUE/WebTransport tabanlı senaryolar) gibi kayıp-toleranslı trafiğe düşük gecikim sunar.

Dağıtım tarafında HTTP/3, Alt-Svc ile uçlara ilan edilerek kademeli benimsenir; istemci, aynı origin için h2/h1’e gerektiğinde geri dönebilir. QUIC’in 0-RTT özelliği, idempotent GET gibi güvenli işlemlerde el sıkışma tamamlanmadan isteğe başlama olanağı verir (yeniden oynatma riskleri nedeniyle sunucu politikalarıyla sınırlandırılır). “Server push” yeteneği standartta bulunsa da pratikte tarayıcı desteği ve kullanım kalıpları sınırlıdır; çoğu dağıtım, 103 Early Hints ve preload gibi mekanizmalarla benzer kazanımları elde eder. Böylece HTTP/3; bağımsız akışlar, QPACK ve QUIC’in modern taşıma özellikleriyle, web trafiğini daha tepki-hızlı, kararlı ve güçlü ağlarda ölçeklenir hale getirir.

6. Ölçülebilirlik ve Yönetilebilirlik: Spin Bit, Invariants

QUIC’in tel görüntüsü (wire image) büyük ölçüde şifrelenir; bu nedenle ara cihazların (NAT, yönlendirici, ölçüm kutuları) paket içeriklerinden ayrıntılı bilgi çıkarması amaçlı olarak zorlaştırılmıştır. Buna karşılık, protokolün invariants olarak adlandırılan ve sürümler arasında değişmeyecek şekilde tanımlanan bazı alanları (örn. “long/short header” ayrımı, sürüm alanının konumu, bağlantı kimliklerinin varlığı) görünür bırakılır. Bu sayede ağlar, bağlantı kimliği (CID) üzerinden yük dengeleme ya da akış yapışkanlığı gibi temel işlemleri, uçtan uca şifrelemeyi bozmayacak biçimde gerçekleştirebilir; aynı zamanda protokol evrimini engelleyen “orta nokta sertleşmesi”nin önüne geçilir.

Spin bit, uçların her RTT’de bir kez bit değerini “çevirerek” (toggle) gönderdiği, yalnızca yaklaşık RTT kestirimi için tasarlanmış, isteğe bağlı bir görünen alandır. Bir gözlem noktası, ardışık paketlerdeki bit değişimlerini izleyerek uçtan uca veya yön başına gecikim hakkında kaba bir tahmin yapabilir. Ancak spin bit, aktif veri akışı yokken, yoğun paket yeniden sıralaması/kaybı altında ya da bağlantı göçü sonrasında güvenilir ölçüm vermez; bant genişliği, kayıp oranı, uygulama seviyesi gecikim gibi metrikleri doğrudan vermez. Bu nedenle gizlilik ve parmak izi çıkarma risklerini azaltmak için uygulamalar spin bit’i örnekleme (sampling) ile açıp kapatabilir veya belirli oranlarda tamamen devre dışı bırakabilir.

Yönetilebilirlik açısından RFC 9312, pasif ölçümün sınırlı kaldığı ortamlarda uç iş birliğine dayalı yöntemleri önerir: örneğin qlog ile ayrıntılı olay kaydı, uçların sağladığı sayaç/metrik arabirimleri, ECN sayaçlarının raporlanması ve kontrol kanallarında asgari görünür sinyallerin korunması. Operasyon ekipleri için pratik hedef; “paket içeriğini görmeden” bile RTT eğilimleri, tıkanıklık belirtileri ve ulaşılabilirlik sorunları hakkında yeterli gözlem elde etmek, bunu da gizlilik ilkeleriyle dengede tutmaktır. Ayrıca CID rotasyonu ve bağlantı göçü gibi QUIC özelliklerinin, pasif korelasyonu zorlaştıracağı unutulmamalıdır; yük dengeleyicilerde CID tabanlı eşlemenin doğru yapılandırılması yönetimsel istikrar için kritik önemdedir.

Saha pratiği olarak: (i) Spin bit’i yalnızca tanılama/sorun giderme dönemlerinde veya istatistiksel örnekleme ile açmak; (ii) Kalıcı telemetri için qlog ve uç metrikleri gibi iş birliği gerektiren yöntemleri tercih etmek; (iii) Ölçümlerin yol değişimi, NAT yeniden bağlama ve farklı RTT’li çoklu erişim senaryolarında anlamını yitirebileceğini hesaba katmak önerilir. Böylece QUIC, hem gizliliği önceleyen tasarımını korur hem de işletmecilere yeterli gözlemlenebilirlik sağlayacak, sınırlı ama işe yarar araçlar sunar.

7. QUIC’in Avantajları

  1. Düşük el sıkışma gecikmesi: 1-RTT kurulum, 0-RTT yeniden başlatma. 
  2. Akış bazında HOL Blocking yok: HTTP/3’te tek akıştaki kayıp diğer akışları durdurmaz. 
  3. Bağlantı göçü: Mobil senaryolarda IP/port değişse de bağlantı sürer. 
  4. Genişletilebilirlik: DATAGRAM, sürüm müzakeresi, QUIC v2. 
  5. Güvenlik varsayılan: TLS 1.3 zorunlu; anahtar güncelleme, anti-amplification, adres doğrulama.

8. Zorluklar ve Dezavantajlar

  • UDP engelleri / QoS: Bazı ağlar UDP’yi sınırlayabilir; firewall/NAT zaman aşımı ve MTU konuları dikkat ister.
  • Operasyonel görünürlük: Şifreli tel görüntüsü tanılama/ölçüm araçlarını zorlar; spin bit isteğe bağlıdır. 
  • CPU maliyeti: Kullanıcı alanı tıkanıklık kontrolü ve AEAD şifreleme CPU’yu artırabilir; ancak modern uygulamalarda maliyet/performans dengesi genellikle olumludur.
  • Anti-amplification ve sertifika boyutu: 3× limiti ve büyük sertifika zincirleri ilk RTT’yi uzatabilir; zinciri optimize etmek önemlidir.

9. Diğer Protokollerle Karşılaştırma

  • TCP + TLS + HTTP/2: Geniş dağıtım olgunluğu yüksek; fakat HOL Blocking, yavaş başlatma ve çok akışı tek bytestream üzerinde taşımanın bedeli var. QUIC/HTTP/3 bu sınırlamaları hedefler. 
  • SCTP (+ DTLS): Akış kavramı QUIC’e benzer; ancak dağıtım ve orta nokta uyumluluğu TCP kadar yaygın değildir. QUIC, UDP üzerinden, tarayıcı desteği ve HTTP/3 ile pratikte daha geniş ekosistem buldu.
  • MPTCP: Çok yol desteğini TCP düzeyinde sunar; QUIC’te bağlantı göçü yerleşik, multipath QUIC ise aktif standartlaşma alanıdır (taslak).

10. Go ile QUIC: quic-go ile “Ham” QUIC ve HTTP/3

Aşağıdaki örnekler quic-go API’sına uygundur. HTTP/3 için quic-go/http3 paketindeki ListenAndServeQUIC ve http3.Transport kullanılır. QUIC DATAGRAM API’sı EnableDatagram, SendDatagram ve ReceiveDatagram ile etkinleştirilir.

Not: Örnekler yerel geliştirme içindir. Sertifika üretimi için openssl veya mkcert ile server.crt/server.key hazırlayınız. Yerel denemede istemcide TLS doğrulamasını basitleştirmek için InsecureSkipVerify kullanılmıştır; production’da kullanmayınız.

10.1. Minimal QUIC Echo Sunucu (Streams)

/ go.mod
// module example.com/quic-echo
// go 1.22
// require github.com/quic-go/quic-go v0.55.0

// server.go
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"log"
	"os"
	"os/signal"
	"syscall"

	"github.com/quic-go/quic-go"
)

func main() {
	// Load TLS certificate and key. For local testing, generate a self-signed cert.
	tlsConf, err := tlsConfig("server.crt", "server.key")
	if err != nil {
		log.Fatalf("failed to load TLS config: %v", err)
	}

	// Enable QUIC DATAGRAMs (optional) and allow 0-RTT resumption (server-side).
	quicConf := &quic.Config{
		EnableDatagram: true,
		Allow0RTT:      true,
	}

	addr := "localhost:4242"
	ln, err := quic.ListenAddr(addr, tlsConf, quicConf)
	if err != nil {
		log.Fatalf("listen failed: %v", err)
	}
	log.Printf("QUIC echo server listening on %s", addr)

	// Graceful shutdown on Ctrl+C.
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
	defer stop()

	go func() {
		<-ctx.Done()
		ln.Close() // closes the listener (accepted connections remain until closed)
	}()

	for {
		conn, err := ln.Accept(context.Background())
		if err != nil {
			if ctx.Err() != nil {
				log.Printf("shutting down listener...")
				return
			}
			log.Printf("accept error: %v", err)
			continue
		}
		log.Printf("accepted new QUIC connection from %s", conn.RemoteAddr())
		go handleConn(conn)
	}
}

func handleConn(conn quic.Connection) {
	defer conn.CloseWithError(0, "bye")

	// Optionally check whether DATAGRAMs were negotiated.
	cs := conn.ConnectionState()
	log.Printf("0-RTT used? %v, DATAGRAM supported? %v", cs.Used0RTT, cs.SupportsDatagrams)

	// Accept streams in a loop. Each stream is independent.
	for {
		str, err := conn.AcceptStream(context.Background())
		if err != nil {
			log.Printf("accept stream: %v", err)
			return
		}
		go echo(str)
	}
}

func echo(s quic.Stream) {
	defer s.Close()
	buf := make([]byte, 32*1024)
	for {
		n, err := s.Read(buf)
		if n > 0 {
			// Echo back the same payload.
			if _, werr := s.Write(buf[:n]); werr != nil {
				log.Printf("write error: %v", werr)
				return
			}
		}
		if err == io.EOF {
			return
		}
		if err != nil {
			log.Printf("read error: %v", err)
			return
		}
	}
}

func tlsConfig(certFile, keyFile string) (*tls.Config, error) {
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return nil, err
	}
	// Configure ALPN for your application if needed (e.g., "hq-interop").
	return &tls.Config{
		Certificates: []tls.Certificate{cert},
		// NextProtos can be customized. For raw-QUIC apps, pick an ALPN token.
		NextProtos: []string{"hq-example"},
	}, nil
}
// client.go
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"log"
	"time"

	"github.com/quic-go/quic-go"
)

func main() {
	// WARNING: InsecureSkipVerify is ONLY for local testing!
	tlsConf := &tls.Config{
		InsecureSkipVerify: true,
		NextProtos:         []string{"hq-example"},
	}

	quicConf := &quic.Config{
		EnableDatagram: true,
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	addr := "localhost:4242"
	conn, err := quic.DialAddr(ctx, addr, tlsConf, quicConf)
	if err != nil {
		log.Fatalf("dial failed: %v", err)
	}
	defer conn.CloseWithError(0, "client done")

	// Open a bidirectional stream and send a message.
	stream, err := conn.OpenStream()
	if err != nil {
		log.Fatalf("open stream: %v", err)
	}
	defer stream.Close()

	msg := []byte("hello over QUIC streams")
	if _, err := stream.Write(msg); err != nil {
		log.Fatalf("write: %v", err)
	}

	// Read echo.
	reply := make([]byte, len(msg))
	if _, err := io.ReadFull(stream, reply); err != nil {
		log.Fatalf("read: %v", err)
	}
	fmt.Printf("echo reply: %q\n", string(reply))

	// Optionally try a DATAGRAM (best-effort, not retransmitted).
	if err := conn.SendDatagram([]byte("ping-dgram")); err == nil {
		// ReceiveDatagram blocks; in real apps, run this in a goroutine.
		ctx2, cancel2 := context.WithTimeout(context.Background(), time.Second)
		defer cancel2()
		if dg, err := conn.ReceiveDatagram(ctx2); err == nil {
			fmt.Printf("received datagram: %q\n", string(dg))
		}
	}
}

EnableDatagram ile RFC 9221 DATAGRAM uzantısı müzakere edilir (karşı taraf da desteklemeli). SendDatagram/ReceiveDatagram ile gönderilen veriler şifreli ve tıkanıklık kontrolüne tabi, ancak yeniden iletilmez.

10.2. Minimal HTTP/3 Sunucu ve İstemci

Sunucu:

// go.mod
// module example.com/h3
// go 1.22
// require github.com/quic-go/quic-go v0.55.0

package main

import (
	"crypto/tls"
	"fmt"
	"log"
	"net/http"

	"github.com/quic-go/quic-go"
	"github.com/quic-go/quic-go/http3"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
		// You can detect 0-RTT by checking r.TLS.HandshakeComplete (server might have accepted request early).
		if r.TLS != nil && !r.TLS.HandshakeComplete {
			w.Header().Set("x-served-in-0rtt", "true")
		}
		fmt.Fprintln(w, "Hello over HTTP/3")
	})

	server := &http3.Server{
		Handler: mux,
		Addr:    ":4433",
		// Configure ALPN to "h3" and set QUIC options.
		TLSConfig:  http3.ConfigureTLSConfig(&tls.Config{Certificates: loadCert()}),
		QUICConfig: &quic.Config{EnableDatagram: true, Allow0RTT: true},
	}
	log.Printf("HTTP/3 server on https://localhost:4433/hello")
	log.Fatal(server.ListenAndServe())
}

func loadCert() []tls.Certificate {
	cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
	if err != nil {
		panic(err)
	}
	return []tls.Certificate{cert}
}

İstemci:

package main

import (
	"crypto/tls"
	"fmt"
	"io"
	"log"
	"net/http"

	"github.com/quic-go/quic-go/http3"
)

func main() {
	// http3.Transport implements http.RoundTripper for HTTP/3.
	tr := &http3.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,                // For local testing only
			NextProtos:         []string{http3.NextProtoH3}, // Set ALPN to "h3"
		},
	}
	defer tr.Close()

	c := &http.Client{Transport: tr}
	resp, err := c.Get("https://localhost:4433/hello")
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	b, _ := io.ReadAll(resp.Body)
	fmt.Printf("status=%s body=%q\n", resp.Status, string(b))
}

ListenAndServeQUIC / http3.Server ve istemci tarafında http3.Transport resmi dokümantasyonda bu şekilde kullanılır; TLSConfig için sunucuda http3.ConfigureTLSConfig kullanmak doğru ALPN’yi (h3) otomatik yerleştirir.


11. Uygulama İpuçları ve En İyi Pratikler

  • Sertifika zinciri boyutunu optimize edin: Anti-amplification nedeniyle ilk cevap boyutu 3× sınırına tabidir; büyük zincirler ek RTT doğurabilir. 
  • ALPN ve QUIC sürüm desteği: HTTP/3 için h3 ALPN’si; sürüm müzakeresi için RFC 9368 desteği. 
  • Datagram vs. Stream seçimi: Kayba toleranslı, düşük gecikim odaklı telemetri/oyun/medya için DATAGRAM; güvenilirlik şartsa stream. 
  • Bağlantı göçü testleri: NAT rebinding / arayüz değişimlerini (Wi-Fi→LTE) sahada test edin; path doğrulaması ve CID rotasyonu önemlidir. 
  • Gözlemlenebilirlik: qlog etkinleştirin; ops ekipleri için RFC 9312 rehberini izleyin.

El sıkışma uçuşunu küçük tutmak üretimde fark yaratır. Sertifika zincirini mümkün olduğunca kısaltın: gereksiz ara sertifikaları atın, aynı CA’nın birden çok zincirinden yalnızca en kısa olanı gönderin, OCSP stapling ve (mümkünse) TLS certificate compression kullanın. Büyük RSA zincirleri yerine eşdeğer güvenlikte ECDSA tercih etmek tipik olarak byte sayısını ciddi azaltır. Bu iyileştirmeler, sunucunun istemci doğrulanana kadar yalnızca aldığı baytın 3 katını gönderebildiği anti-amplification rejiminde ekstra RTT ödenmesini önler. İlk temasın hafiflemesi için session resumption, NEW_TOKEN dağıtımı ve Retry gereksinimini minimize eden topolojiler de pratikte etkilidir.

Protokol pazarlığı tarafında ALPN ve sürüm desteği uyumlu ilerlemeli. HTTP için h3’ü ilan ederken, istemcinin ve ara cihazların olgunluğu nedeniyle h2 geri dönüş yolunu açık tutun. Taşıma katında uyumlu sürüm müzakeresi desteği ve QUIC v1 + v2’yi birlikte sunmak, orta nokta sertleşmesine karşı dayanıklılığı artırır. Bu esnada yük dengeleyicilerin Connection ID (CID) temelli eşlemesi tutarlı olmalı; farklı uçlarda aynı origin için aynı TLS/QUIC parametreleri (idle timeout, max datagram size, TLS grupları) taşınmazsa sürüm/ALPN pazarlıklarında sürpriz uyumsuzluklar doğar.

Uygulama veri taşımasını doğru taşıma soyutlaması ile eşleyin: Stream’ler güvenilir, sıralı (ve gerektiğinde akış-bazlı akış kontrolüyle) içerikler için idealdir; tipik HTTP gövdeleri, API yanıtları, dosya parçaları burada taşınmalı. DATAGRAM, yeniden iletilmeyen ama şifreli ve tıkanıklık kontrolüne tabi “en iyi çaba” trafiğe uygundur: oyun telemetrisi, düşük gecikimli denetim mesajları, bazı medya/tünelleme senaryoları. Eğer uygulamanız DATAGRAM üzerinde “biraz güvenilirlik” istiyorsa, bunu uygulama katında hafif onaylama/yeniden gönderme ile yapın; aksi halde gereksinimleriniz aslında stream’e işaret ediyordur. Her iki durumda da MTU farkındalığına dikkat edin (DPLPMTUD varsa etkinleştirin); parçalanma, jitter ve kaybı büyütür.

Bağlantı göçü ve NAT rebinding sahada mutlaka test edilmeli. Cihazların Wi-Fi↔LTE geçişlerinde PATH_CHALLENGE/RESPONSE’un devreye girip akışların kesintisiz sürdüğünü, yeni yolda pencere/RTT’nin güvenli biçimde yeniden öğrenildiğini doğrulayın; düzenli CID rotasyonu yapılandırmasını ve yük dengeleyiciyle uyumunu kontrol edin. Gözlemlenebilirlik için üretimde qlog’u örnekleme (sampling) ile açın; PTO sayısı, 0-RTT kabul oranı, handshake başarısı, ortalama/percentile RTT, kayıp ve ECN/CE oranları gibi metrikleri izleyin. Sorun giderirken spin bit’i geçici ve sınırlı oranlarda kullanın; kalıcı telemetriyi ise qlog ve uç-taraf metrik arayüzleriyle sağlayın. Bu pratiklerle QUIC dağıtımınız hem hızlı açılır hem de operasyonel olarak öngörülebilir kalır.


QUIC, modern internet uygulamalarının beklentilerine uygun düşük gecikmeli, güvenli ve esnek bir taşıma katmanı sunar. HTTP/3’ün başarısı, QUIC’in akış izolasyonu, 0-RTT, bağlantı göçü ve genişletilebilirlik tasarımlarıyla yakından ilişkilidir. Operasyonel tarafta şifreli tel görüntüsü yeni ölçüm pratiklerini gerektirirken, standarta eklenen spin bit ve yönetilebilirlik rehberleri bu boşluğu doldurmaya çalışır. QUIC v2 ve sürüm müzakeresi, uzun vadede ossification’ı kırmayı hedefler. Uygulama geliştiricisi için quic-go gibi olgun kütüphanelerle ham QUIC ve HTTP/3 uygulamak artık oldukça erişilebilir durumdadır.


Kaynaklar