Photo by Amy Lister on Unsplash

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.RoundTrippeile 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.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Furkan Güngör
Furkan Güngör

Written by Furkan Güngör

Solution Developer — I want to change the world, give me the source code.

No responses yet

Write a response