Skip to content

Replace Hackney with Req as default HTTP client - #179

Closed
bglusman wants to merge 3 commits into
lau:masterfrom
bglusman:port-pr-165-replace-hackney-with-req
Closed

Replace Hackney with Req as default HTTP client#179
bglusman wants to merge 3 commits into
lau:masterfrom
bglusman:port-pr-165-replace-hackney-with-req

Conversation

@bglusman

Copy link
Copy Markdown

Purpose

Port and adapt lau/tzdata#165 onto this fork so Req becomes tzdata's default HTTP client and Hackney is no longer a required production dependency. This prepares SCHIP for a follow-up change that can update its tzdata ref and remove Hackney separately.

The original commit by Ryan Spore, co-authored by Matt Wynne, is preserved in this branch's history.

Implementation

  • Adds Tzdata.HTTPClient.Req and makes it the default adapter.
  • Supports Req >= 0.5.10 and < 1.0.0, including SCHIP's locked Req 0.6.2.
  • Preserves the existing HTTP-client tuple contract, redirect behavior for GET and HEAD, repeated response headers, custom headers, and transport errors.
  • Removes the unused extra Tzdata.Finch pool; Req supervises and uses its own pool.
  • Keeps the Hackney adapter available only when explicitly configured and Hackney is present at runtime, without retaining a Hackney dependency edge.
  • Raises the minimum Elixir version to 1.14 and aligns GitHub Actions/Travis configuration with that requirement.
  • Replaces network-bound HTTPBin unit tests with deterministic Req.Test coverage; real IANA requests remain available as explicitly included integration tests.

Validation

  • mix format --check-formatted on all changed Elixir files — passed.
  • mix deps.unlock --check-unused — passed.
  • mix test test/tzdata/http_client/req_test.exs — 7/7 passed with locked Req 0.5.17, SCHIP's Req 0.6.2, and Req 0.7.2.
  • mix test test/integration/req_download_test.exs --include integration — 5/5 passed against IANA with Req 0.5.17, 0.6.2, and 0.7.2.
  • mix test --exclude doctest — 49 passed, 5 skipped, 22 excluded.
  • mix deps.tree --only prod — Req/Finch production tree contains no Hackney.
  • Full mix test under local Elixir 1.20/OTP 29: 64/65 passed; the single failing historical-transition doctest is pre-existing and reproduces unchanged on origin/master under OTP 29. The draft PR's Elixir 1.14/OTP 25 GitHub Actions run will be the supported-runtime check.
  • Local mix compile --warnings-as-errors under Elixir 1.20/OTP 29 is blocked by pre-existing/dependency warnings, including the existing unused require Tzdata.Util; the supported-runtime GitHub Actions compile check is pending.

Change Impact

Scope: diff
Files changed: 12
Functions changed: 8
Cross-app: no
Depth reached: 3
Test files identified: 2

Affected Areas

  • Tzdata.DataLoader.http_client/0 changes the default adapter used by downloads, HEAD metadata, and file-size checks.
  • Tzdata.HTTPClient.Req.get/3, head/3, and request/4 provide the new default Req implementation.
  • Tzdata.HTTPClient.Hackney.get/3, head/3, and ensure_hackney!/0 preserve Hackney as an explicitly configured legacy adapter with runtime dependency checks.
  • Tzdata.ReleaseUpdater and Tzdata.DataBuilder are downstream automatic IANA polling, download, and release-loading paths.
  • Tzdata.Mixfile changes the dependency, configured adapter, and supported Elixir baseline.
  • test/tzdata/http_client/req_test.exs and test/integration/req_download_test.exs cover the new adapter.

Category Hints

  • None.

Risk Assessment

Risk level: medium

Rationale: This changes an existing shared runtime path for every consumer using tzdata's default automatic-update behavior, including its external IANA integration and supported Elixir baseline. Focused unit and tagged integration coverage keep the change below high risk.

Key Factors

  • Default HTTP behavior changes from Hackney to Req across download and metadata-check paths.
  • Dependency and minimum-runtime changes can affect downstream consumers at compile or deployment time.
  • Cicada traced the affected path through Tzdata.DataLoader, Tzdata.ReleaseUpdater, and Tzdata.DataBuilder.
  • Seven deterministic adapter tests cover responses, redirects, repeated/custom headers, and transport errors; five opt-in integration tests exercise IANA.
  • Hackney remains explicitly configurable for backward compatibility.

Skills Used

  • opening-pull-requests v1.1.0
  • analyzing-change-impact (unversioned; last reviewed 2026-06-04)
  • developing-with-tdd (unversioned; last reviewed 2026-06-01)
  • searching-elixir-code (unversioned; last reviewed 2026-01-24)
  • writing-elixir-code (unversioned; last reviewed 2026-06-01)

patrols and others added 3 commits June 11, 2026 23:37
Erlang/OTP 29 tightened :calendar.time_to_seconds/1 to reject hours
outside 0..23. The IANA tz database legally uses "24:00" for end-of-day
transition boundaries, which time_for_rule/transform_until_datetime parse
to an hour-24 datetime (e.g. {{2024, 3, 31}, {24, 0, 0}}). Passing that to
:calendar.datetime_to_gregorian_seconds/1 — which calls time_to_seconds/1
internally — crashed period building on OTP 29 with a FunctionClauseError.
OTP <= 28 accepted it and returned the value for the equivalent next-day
00:00.

Add Tzdata.Util.datetime_to_gregorian_seconds/1, which computes the value
directly (date_to_gregorian_days * 86400 + h*3600 + m*60 + s) so any hour
(including 24) is handled, matching the pre-OTP-29 result. Route both
Tzdata.Util.datetime_to_utc/3 and Tzdata.PeriodBuilder.datetime_to_utc/3
through it. Add a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Addresses security concerns with Hackney (CVE-2018-1000007,
AIKIDO-2026-10122). Req provides a modern, actively maintained
HTTP client built on Mint.

Changes:
- Add Req as required dependency, remove Hackney
- Implement Tzdata.HTTPClient.Req with redirect support
- Set Req as default HTTP client in configuration
- Hackney implementation remains available for backward compatibility
- Update README with migration information

Co-Authored-By: Matt Wynne <[email protected]>
@bglusman

Copy link
Copy Markdown
Author

Opened against the parent repository by mistake while preparing a fork-specific port. Closing this draft; the intended PR targets bglusman/tzdata.

@bglusman bglusman closed this Aug 19, 2026
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.

3 participants