4
Klien HTTP Dasar
httpclient.Client adalah implementasi default dari interface heimdall.Client untuk melakukan panggilan HTTP. Struct ini membungkus sebuah heimdall.Doer, menyimpan daftar plugin, dan menampung seluruh state retry. Baris var _ heimdall.Client = (*Client)(nil) memastikan struct ini selalu memenuhi interface tersebut. httpclient/client.go:16-251 httpclient/client.go:322
Interface heimdall.Client hanya berisi lima method pembungkus HTTP, satu method Do, dan satu method AddPlugin, sehingga tipe lain yang memenuhi kontrak yang sama bisa dipakai bergantian dengan httpclient.Client. client.go:15-233 Kontrak Doer yang lebih kecil hanya menuntut satu method Do(*http.Request) (*http.Response, error). client.go:10-124
Komposisi struct
Diagram berikut menunjukkan bagaimana NewClient mengisi field Client: sebagian field punya nilai default, sebagian lain hanya terisi lewat Option.
graph TD
NewClient["httpclient.NewClient"] --> Client["Client"]
NewClient --> NetClient["*http.Client (Timeout = defaultHTTPTimeout)"]
NewClient --> Opt["httpclient.Option"]
Opt --> Timeout["timeout *time.Duration"]
Opt --> Retrier["retrier heimdall.Retriable"]
Opt --> RetryCount["retryCount int"]
Opt --> Codes["retryableCodes []int"]
Opt --> Budget["retryErrorBudget *internal.ErrorBudget"]
Client --> Doer["client heimdall.Doer"]
Client --> Plugins["plugins []heimdall.Plugin"]
Client --> Timeout
Client --> Retrier
Client --> RetryCount
Client --> Codes
Client --> Budget
NetClient --> Doer
style NetClient fill:#336791,color:#fff
style Client fill:#f59e0b,color:#000
style Opt fill:#f59e0b,color:#000
style Doer fill:#8b5cf6,color:#fff
style Plugins fill:#8b5cf6,color:#fff
style Timeout fill:#f59e0b,color:#000
style Retrier fill:#f59e0b,color:#000
style RetryCount fill:#f59e0b,color:#000
style Codes fill:#f59e0b,color:#000
style Budget fill:#f59e0b,color:#000
%% fig: Komposisi struct httpclient.Client
%% desc: NewClient mengisi field Client lewat Option, sementara tanpa opsi klien memakai *http.Client dengan timeout default dan NoRetrier.client bertipe heimdall.Doer sehingga implementasi net/http bisa diganti sepenuhnya; plugins menampung semua heimdall.Plugin; retryableCodes menyimpan status code tambahan yang boleh diulang; dan retryErrorBudget menunjuk ke internal.ErrorBudget yang membatasi retry. httpclient/client.go:16-251
Field dan tipe
| Field | Tipe | Peran |
|---|---|---|
client | heimdall.Doer | Eksekutor HTTP yang sebenarnya dipanggil di dalam Do |
plugins | []heimdall.Plugin | Daftar plugin yang menerima callback request |
timeout | *time.Duration | Nilai timeout yang diterapkan ke *http.Client |
retrier | heimdall.Retriable | Sumber interval jeda antar percobaan |
retryCount | int | Jumlah maksimum percobaan ulang |
retryableCodes | []int | Status code tambahan yang dianggap layak diulang |
retryErrorBudget | *internal.ErrorBudget | Pembatas retry berbasis error budget |
Method pembungkus HTTP
Lima method pembungkus membuat *http.Request sendiri, menyalin headers ke request.Header, lalu menyerahkan eksekusinya ke Do. Get dan Delete mengirim body nil, sedangkan Post, Put, dan Patch menerima body io.Reader. httpclient/client.go:57-675 httpclient/client.go:69-1066 httpclient/client.go:109-1197
| Method | Signature | Body request |
|---|---|---|
Get | Get(url string, headers http.Header) | nil |
Post | Post(url string, body io.Reader, headers http.Header) | io.Reader |
Put | Put(url string, body io.Reader, headers http.Header) | io.Reader |
Patch | Patch(url string, body io.Reader, headers http.Header) | io.Reader |
Delete | Delete(url string, headers http.Header) | nil |
Do | Do(req *http.Request) | ditentukan pemanggil |
Kegagalan membuat request tidak dikirim ke jaringan. Setiap method mengembalikan *http.Response bernilai nil beserta error yang dibungkus dengan pesan seperti "GET - request creation failed: %w". httpclient/client.go:57-675
graph LR
Get["Get / Delete"] --> NewReq["http.NewRequest dengan body nil"]
Body["Post / Put / Patch"] --> NewReq2["http.NewRequest dengan body io.Reader"]
NewReq --> Header["request.Header = headers"]
NewReq2 --> Header
Header --> Do["Client.Do(request)"]
Do --> Doer["heimdall.Doer.Do"]
Do --> Plugins["reportRequestStart / reportRequestEnd / reportError"]
style Get fill:#2d7d46,color:#fff
style Body fill:#2d7d46,color:#fff
style NewReq fill:#8b5cf6,color:#fff
style NewReq2 fill:#8b5cf6,color:#fff
style Header fill:#8b5cf6,color:#fff
style Do fill:#f59e0b,color:#000
style Doer fill:#336791,color:#fff
style Plugins fill:#8b5cf6,color:#fff
%% fig: Delegasi method pembungkus ke Client.Do
%% desc: Setiap method pembungkus menyiapkan *http.Request lalu menyerahkan eksekusi ke Do, yang memanggil Doer dan melaporkan event ke plugin.Do juga menutup body request asli setelah pemanggilan selesai, sehingga pemanggil tidak perlu menutup body request yang dioper ke sana. httpclient/client.go:121-1288 Karena Do menerima *http.Request yang sudah jadi, header dan body sepenuhnya menjadi tanggung jawab pemanggil pada jalur ini. Rangkaian percobaan ulang di dalam Do beserta urutan callback plugin dibahas di Siklus Request dan Retry.
Nilai default dan timeout
Dua konstanta menentukan perilaku awal klien: defaultRetryCount bernilai 0 dan defaultHTTPTimeout bernilai 30 * time.Second. httpclient/client.go:27-309
NewClient membuat http.Client dengan Timeout: defaultHTTPTimeout, memakai defaultRetryCount, dan memasang heimdall.NewNoRetrier() sebagai retrier. Setelah semua Option dijalankan, updateHTTPTimeout dipanggil sekali lagi. httpclient/client.go:34-4910
| Field | Nilai default | Sumber |
|---|---|---|
retryCount | defaultRetryCount = 0 | httpclient/client.go:27-309 |
http.Client.Timeout | defaultHTTPTimeout = 30 * time.Second | httpclient/client.go:27-309 |
retrier | heimdall.NewNoRetrier() | httpclient/client.go:34-4910 |
WithHTTPTimeout menyimpan durasi ke field timeout lalu langsung memanggil updateHTTPTimeout, bukan menimpa http.Client secara langsung. httpclient/options.go:14-2111
updateHTTPTimeout hanya menulis ke http.Client bila field client benar-benar bertipe *http.Client; jika tidak, nilai timeout diabaikan tanpa error. httpclient/client.go:222-23012
⚠️ Memasang
Doerkustom lewatWithHTTPClientmembuatWithHTTPTimeouttidak berpengaruh, karenaDoertersebut bukan*http.Client. Timeout pada kasus itu harus ditangani sendiri oleh implementasiDoer. httpclient/client.go:222-23012
Opsi lain yang mengisi field di atas, termasuk WithRetryCount dan WithRetryableStatusCodes, dijelaskan di Option pada httpclient.
Menambahkan plugin
AddPlugin hanya menambahkan plugin ke slice plugins; tidak ada validasi dan tidak ada penggantian plugin yang sudah ada. httpclient/client.go:51-5413 Setiap plugin wajib memenuhi interface heimdall.Plugin dengan tiga method: OnRequestStart, OnRequestEnd, dan OnError. plugin.go:9-1314
Ketiga method itu dipanggil lewat tiga helper terpisah yang mengiterasi seluruh isi plugins: reportRequestStart sebelum request dieksekusi, reportRequestEnd setelah response diperoleh, dan reportError saat terjadi error. httpclient/client.go:204-22015
graph TD
AddPlugin["Client.AddPlugin"] --> Plugins["plugins []heimdall.Plugin"]
Plugins --> Start["reportRequestStart"]
Plugins --> End["reportRequestEnd"]
Plugins --> Err["reportError"]
Start --> OnStart["Plugin.OnRequestStart"]
End --> OnEnd["Plugin.OnRequestEnd"]
Err --> OnError["Plugin.OnError"]
style AddPlugin fill:#f59e0b,color:#000
style Plugins fill:#8b5cf6,color:#fff
style OnStart fill:#2d7d46,color:#fff
style OnEnd fill:#2d7d46,color:#fff
style OnError fill:#dc2626,color:#fff
style Start fill:#8b5cf6,color:#fff
style End fill:#8b5cf6,color:#fff
style Err fill:#dc2626,color:#fff
%% fig: Jalur plugin pada httpclient.Client
%% desc: AddPlugin menambah elemen ke slice plugins, lalu helper reportRequestStart, reportRequestEnd, dan reportError meneruskan event ke setiap plugin.Contoh plugin dan logger yang siap dipakai ada di Plugin dan Request Logger.
Contoh pemakaian
Pemakaian paling ringkas membuat klien dengan timeout, jumlah retry, dan retrier, lalu memanggil Get dengan headers yang sudah disiapkan. Response harus dibaca dari response.Body dan ditutup oleh pemanggil. examples/client.go:18-4316
timeout := 100 * time.Millisecond
httpClient := httpclient.NewClient(
httpclient.WithHTTPTimeout(timeout),
httpclient.WithRetryCount(2),
)
response, err := httpClient.Get(baseURL, headers)
Pola yang sama dipakai di README: klien dibuat dengan httpclient.NewClient(httpclient.WithHTTPTimeout(timeout)), Get dipanggil dengan nil header, dan body dibaca memakai io.ReadAll. README.md:52-6517