
Deep Dive to http.RoundTripper
Golang’ın HTTP istemci kütüphanesi (net/http
), varsayılan olarak bir http.Client
ve HTTP isteklerini işleyen bir taşıma katmanı (Transport
) sağlar. Bu taşıma katmanı, http.RoundTripper
arayüzü ile temsil edilir ve HTTP isteklerinin nasıl işlendiğini özelleştirmek için güçlü bir araç sunar.
RoundTripper
basit bir tanımla http.Client
için bir middleware
gibidir. Çünkü http.Client
doğrudan HTTP isteklerini işlemez, bunun yerine tüm istekleri http.RoundTripper
arayüzüne gönderir.
type RoundTripper interface {
RoundTrip(*http.Request) (*http.Response, error)
}
Although, it is possible to do anything within the
RoundTrip
method (as in like middleware for your HTTP handlers), it is recommended you don't inspect the response, return an error (nil or non nil) and shouldn't do stuffs like user auth (or cookies handling)..
Why(?)
http.RoundTripper
, http.Client
tarafından kullanılan düşük seviyeli bir arabirimdir ve isteği yapıp yanıtı döndürmekle sorumludur.
Geliştirdiğimiz uygulamalarda onlarca farklı servisten bilgiye ihtiyaç duyabiliriz. Bu servisler ile haberleşme yöntemlerinden biri de ilgili servise bir HTTP isteği başlatmaktır. Uygulamalarınızda tüm HTTP istekleriniz ve/veya sadece belirli istekler çalışmadan önce araya girip bazı değişiklikler ve/veya işlemler yapmak isteyebilirsiniz. Bu senaryoları biraz daha somutlaştırmak gerekirse;
- Logging
- Header Manipulation
- Retry
- Rate Limiting
- Cache
gibi konular örnek verilebilir.
Implementation
Bu başlık altında farklı kullanım örnekleri ile http.RoundTripper
arabirimi için örnekler sunmaya çalışacağım.
Örnek 1 (Logging)
Bu örnekte müşteri verilerini üçüncü taraf bir API’den çektiğimizi düşünelim. Ancak, bazı isteklerin başarısız olduğunu ve neden başarısız olduğunu http.RoundTrippe
ile yakalamaya çalışalım.
package custom
import (
"log"
"net/http"
"time"
)
type LogRoundTripper struct {
Transport http.RoundTripper
}
func (l *LogRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
start := time.Now()
resp, err := l.Transport.RoundTrip(req)
duration := time.Since(start)
if err != nil {
log.Printf("Request failed: %v", err)
return nil, err
}
log.Printf("Request to %s took %v", req.URL, duration)
return resp, nil
}
LogRoundTripper
için RoundTrip
arayüzünü implemente eden yukarıda kod client.Do
satırı çalıştığında bir middleware
gibi araya girerek bir timer başlatır ve sonuçları loglar.
package main
import (
"http-roundTripper/custom"
"net/http"
)
func main() {
client := &http.Client{
Transport: &custom.LogRoundTripper{
Transport: http.DefaultTransport,
},
}
req, _ := http.NewRequest("GET", "https://jsonplaceholder.typicode.com/posts", nil)
resp, _ := client.Do(req)
defer resp.Body.Close()
}
Kullanımı yukarıdaki gibidir. client.Do
komutu çalıştığında http.RoundTripper
devreye girer ve RoundTrip
func çalışır.
2025/02/15 23:21:21 Request to https://jsonplaceholder.typicode.com/posts took 146.935708ms
Örnek 2 (Header Manipulation)
E-ticaret platformunuzda, ödeme sağlayıcısı API’sine bağlanarak müşterilerin ödeme işlemlerini gerçekleştirdiğimizi düşünelim. Ancak, her istekte yetkilendirme token’ını eklemeyi unutmamak gerekiyor. Bir RoundTripper
kullanarak tüm HTTP isteklerine otomatik olarak Authorization
başlığını ekleyerek geliştiricilerin manuel token ekleme hatalarından kaçınmasını sağlayabiliriz.
package custom
import "net/http"
type AuthRoundTripper struct {
Transport http.RoundTripper
Token string
}
func (a *AuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Set("Authorization", "Bearer "+a.Token)
return a.Transport.RoundTrip(req)
}
AuthRoundTripper
için RoundTrip
arayüzünü implemente eden yukarıda kod client.Do
satırı çalıştığında bir middleware
gibi araya girerek request.Header
‘a token parametresini ekler.
package main
import (
"http-roundTripper/custom"
"net/http"
)
func main() {
client:= &http.Client{
Transport: &custom.AuthRoundTripper{
Transport: http.DefaultTransport,
},
}
req, _ := http.NewRequest("GET", "https://jsonplaceholder.typicode.com/posts", nil)
resp, _ := client.Do(req)
defer resp.Body.Close()
}
Kullanımı yukarıdaki gibidir. client.Do
komutu çalıştığında http.RoundTripper
devreye girer ve RoundTrip
func çalışır.
Örnek 3 (Retry)
Bir finans uygulaması geliştirdiğimizi ve borsa fiyatlarını üçüncü taraf bir API’den aldığımızı düşünelim. API yoğun saatlerde zaman zaman hata veriyor. Bu durumda, belirli hata kodlarına sahip istekleri otomatik olarak birkaç saniye arayla tekrar deneyecek bir RoundTripper
oluşturarak hizmetin kesintisiz çalışmasını sağlayabiliriz. Böylece, geçici ağ hatalarına karşı uygulamanız daha dayanıklı hale gelir.
package custom
import (
"net/http"
"time"
)
type RetryRoundTripper struct {
Transport http.RoundTripper
retries int
}
func (r *RetryRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
var resp *http.Response
var err error
for i := 0; i < r.retries; i++ {
resp, err = r.Transport.RoundTrip(req)
if err == nil {
return resp, nil
}
time.Sleep(time.Duration(i) * time.Second)
}
return nil, err
}
RetryRoundTripper
için RoundTrip
arayüzünü implemente eden yukarıda kod client.Do
satırı çalıştığında bir middleware
gibi araya girerek eğer request hata alırsa retries
kadar tekrar dener.
package main
import (
"http-roundTripper/custom"
"net/http"
)
func main() {
client := &http.Client{
Transport: &custom.RetryRoundTripper{
Transport: http.DefaultTransport,
Retries: 3,
},
}
req, _ := http.NewRequest("GET", "https://jsonplaceholder.typicode.com/posts", nil)
resp, _ := client.Do(req)
defer resp.Body.Close()
}
Kullanımı yukarıdaki gibidir. client.Do
komutu çalıştığında http.RoundTripper
devreye girer ve RoundTrip
func çalışır.
Örnek 4 (Chaining Multiple)
Yukarıdaki örneklerde olduğu gibi sadece 1 adet roundTripper
kullanmak yerine n tane roundTripper
kullanabilirsiniz. Hepsi birbirinin ardı sıra tanımlanma sırasına göre çalışacaktır. Tabii ne kadar fazla middleware
olursa performansa etkisi de doğru orantılı olarak etkilenir.
package main
import (
"http-roundTripper/custom"
"net/http"
)
func main() {
client := &http.Client{
Transport: &custom.LogRoundTripper{
Transport: &custom.AuthRoundTripper{
Transport: &custom.RetryRoundTripper{
Transport: http.DefaultTransport,
Retries: 3,
},
Token: "my-secret-token",
},
},
}
req, _ := http.NewRequest("GET", "https://jsonplaceholder.typicode.com/posts", nil)
resp, _ := client.Do(req)
defer resp.Body.Close()
}
Tanımlanma sırasına göre client.Do
komutu çalıştığında önce LogRoundTripper
ardından AuthRoundTripper
ve son olarak RetryRoundTripper
çalışacaktır.
Bu makalede, http.RoundTripper
arayüzünün ne olduğunu, nasıl çalıştığını ve özel RoundTripper
kullanarak HTTP isteklerini nasıl özelleştirebileceğinizi öğrendiniz.
✅ http.RoundTripper
kullanarak:
✔ HTTP isteklerinize özel mantık ekleyebilirsiniz.
✔ Logging, retry ve header manipulation gibi işlemleri kolayca entegre edebilirsiniz.
✔ Performans optimizasyonları yapabilirsiniz.