Skip to content

Phase 1: correctness fixes, circuit-breaker hardening, and rename to rhttp - #3

Merged
oswaldom-code merged 15 commits into
mainfrom
develop
Jul 21, 2026
Merged

Phase 1: correctness fixes, circuit-breaker hardening, and rename to rhttp#3
oswaldom-code merged 15 commits into
mainfrom
develop

Conversation

@oswaldom-code

@oswaldom-code oswaldom-code commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Summary

Closes out Phase 1 of the resilient HTTP client: fixes the three known correctness/design bugs, hardens the circuit breaker, keeps the Go 1.21 floor, and renames the module to drop the import stutter. Fast-forward merge (no conflicts).

Highlights

🐛 Correctness fixes (with regression tests)

  • Timeout middleware: cancel the timeout context on Body.Close() instead of on RoundTrip return, so streaming/chunked bodies are no longer aborted mid-read (context canceled).
  • Retry middleware: skip draining/closing the response body on the final attempt, so the returned response stays readable.
  • Circuit breaker (half-open): enforce a bounded number of concurrent probes. Adds MaxHalfOpenRequests (default 1 → true single-probe) and SuccessThreshold (default 1). Fixes the previously incorrect "only one at a time due to mutex" behaviour.

♻️ Refactors

  • Split retryRoundTripper.RoundTrip into canRetry / prepareRetry / drainAndClose, removing the //nolint:gocognit.
  • Split circuit-breaker recordResult into recordClosedResult / recordHalfOpenResult (flat, early-returns) and made the state switch exhaustive.

🔧 Compatibility

  • Swap math/rand/v2math/rand so the go 1.21 floor in go.mod is honoured (no consumer on 1.21 breaks). Adopt the min builtin.

📦 Rename (BREAKING)

  • Module github.com/oswaldom-code/go-httpclientgithub.com/oswaldom-code/rhttp; package moved from the httpclient/ subdirectory to the module root and renamed httpclientrhttp. Sentinel error prefixes are now rhttp:.

⚙️ CI

  • Reactivate .github/workflows/ci.yml (test matrix 1.21/1.22/1.23, lint, build, benchmark).

Verification

  • go build ./..., go test ./... -race, golangci-lint run ./..., gofmt — all clean locally.
  • Benchmarks: backoff strategies still zero-alloc / <10ns.

Oswaldo Montaño and others added 15 commits January 19, 2026 14:16
Add initial project setup with README, Makefile, and configuration files
- Add `cache-dependency-path: go.mod` to all `setup-go` steps in the CI workflow to ensure proper caching.
- Fix a typo in `.golangci.yml` for the gocritic `commentedOutCode` disabled check.
- Remove `go.sum` from the `git diff --exit-code` command in the `.github/workflows/ci.yml` build job to only verify `go.mod`.
- Comment out the entire `.github/workflows/ci.yml` file to temporarily disable the GitHub Actions CI pipeline.
Fix: CI cache dependency paths and correct golangci configuration typo
Core HTTP client with middleware chain and resiliency patterns:
client, options, transport, middleware chain, timeout, retry with
backoff strategies, circuit breaker, rate limiting, logging, metrics,
error classification, fluent RequestBuilder and object pooling.

Includes unit tests, examples and benchmarks. Zero dependencies.
- timeout: cancel the timeout context on Body.Close, not on RoundTrip
  return, so streaming/chunked bodies aren't aborted mid-read
- retry: skip draining/closing the body on the final attempt, since
  that response is returned to the caller

Adds httptest.Server regression tests that read the body after
RoundTrip returns.
Split retryRoundTripper.RoundTrip into canRetry, prepareRetry and
drainAndClose, lowering cognitive complexity below the linter
threshold and removing the //nolint:gocognit directive.
Add MaxHalfOpenRequests (default 1) and SuccessThreshold (default 1)
so Half-Open admits a bounded number of concurrent probes and closes
only after enough consecutive successes. Previously the mutex was
released between allowRequest and recordResult, letting every
concurrent request through in Half-Open.

Also split recordResult into recordClosedResult/recordHalfOpenResult,
order callees before callers, and add concurrency regression tests.
Switch backoff jitter from math/rand/v2 (Go 1.22+) to math/rand so the
stdversion warnings go away while go.mod stays at 1.21. Also adopt the
min builtin in place of manual max-capping.
Move the package from the httpclient/ subdirectory to the module root and
rename it from httpclient to rhttp, dropping the go-httpclient/httpclient
import stutter. Updates go.mod, every import and package clause, the
sentinel error prefixes (rhttp:), README, Makefile, .golangci.yml, CLAUDE.md
and the example.

No behavior change: build, race tests and lint pass under the new path.

BREAKING CHANGE: import path is now github.com/oswaldom-code/rhttp and the
package identifier is rhttp (was .../go-httpclient/httpclient, httpclient).
Uncomment .github/workflows/ci.yml so the test, lint, build and benchmark
jobs run on pull requests and pushes to main. The Go 1.21 matrix job now
passes since backoff no longer imports math/rand/v2.
@oswaldom-code
oswaldom-code merged commit 0641162 into main Jul 21, 2026
7 checks passed
oswaldom-code pushed a commit that referenced this pull request Jul 25, 2026
Main carries the squash of PR #3, whose tree is identical to develop at
2cce947. Develop is a strict superset, so this merge keeps the develop
tree unchanged and only records the ancestry so the release PR applies
cleanly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant