Simple HTTP Client for Go with interceptor support.
go get github.com/morkid/hc- Interceptor pattern for request/response customization
- Request mocking via
TakeOver - Automatic
BaseURLresolution (supports relative paths) - Configurable logging (request, response body, headers)
- Custom logger support
- Configurable timeout
- Configurable TLS certificate verification
- Automatic retry with configurable delay and condition
- JSON response helper
- Resty integration
import "github.com/morkid/hc"
client := hc.New(hc.Config{
BaseURL: "http://example.com",
LogEnabled: true,
Interceptor: func(req *http.Request) error {
req.Header.Add("Accept", "application/json")
// forward to the original request
return nil
},
})
req, _ := http.NewRequest("GET", "/hello-world.json", nil)
res, err := client.Do(req)
log.Println(err)
log.Println(res.StatusCode)Set a BaseURL and use relative paths in your requests. HC will resolve the full URL automatically.
client := hc.New(hc.Config{
BaseURL: "https://dummyjson.com:443/",
})
req, _ := http.NewRequest("GET", "/products", nil)
res, _ := client.Do(req)The interceptor has two modes:
- Return an error — the request is rejected with that error.
- Return an
*hc.Interceptorwith emptyErrorMessageand aTakeOverfunction — the request is intercepted and theTakeOverfunction provides a mock response.
helloWorld := hc.Interceptor{
TakeOver: func(req *http.Request) (res *http.Response, err error) {
return &http.Response{
Body: io.NopCloser(strings.NewReader(`{"message":"hello world"}`)),
Status: "200 OK",
StatusCode: 200,
Proto: "HTTP/1.1",
}, nil
},
}
client := hc.New(hc.Config{
Interceptor: func(req *http.Request) error {
if req.URL.Path == "/hello-world.json" {
return &helloWorld
}
return nil
},
})
req, _ := http.NewRequest("GET", "/hello-world.json", nil)
res, _ := client.Do(req)Retry on failure (error or status >= 500) with a configurable delay.
client := hc.New(hc.Config{
MaxRetries: 3,
RetryDelay: time.Second,
})You can also provide a custom retry condition to control when retries happen:
client := hc.New(hc.Config{
MaxRetries: 3,
RetryDelay: 500 * time.Millisecond,
RetryCondition: func(res *http.Response, err error) bool {
return err != nil || res.StatusCode >= 500
},
})Default is 0 (no retry).
Use hc.JSONResponse to unmarshal the response body directly into a struct.
var result map[string]any
err := hc.JSONResponse(res, &result)| Field | Type | Description |
|---|---|---|
LogEnabled |
bool |
Enable request/response logging |
LogResponseBodyEnabled |
bool |
Enable response body logging |
LogHeaderEnabled |
bool |
Enable request header logging |
LogPrefix |
string |
Prefix for log messages |
Logger |
*log.Logger |
Custom logger instance |
Interceptor |
func(*http.Request) error |
Intercept and customize requests |
Timeout |
int |
Timeout in seconds (default: 30) |
BaseURL |
string |
Base URL for relative path resolution |
InsecureSkipVerify |
bool |
Skip TLS certificate verification |
MaxRetries |
int |
Maximum retry attempts (default: 0 = no retry) |
RetryDelay |
time.Duration |
Delay between retries |
RetryCondition |
func(*http.Response, error) bool |
Custom retry condition (default: retry on error or status >= 500) |
HC's transport can be dropped into Resty, so you get HC's interceptor, logging, and base URL features through Resty's fluent API.
client := hc.New()
restyClient := resty.New()
restyClient.SetTransport(client.Transport)
res, err := restyClient.R().
SetHeader("Accept", "application/json").
Get("http://example.com/hello-world.json")