Replace Hackney with Req as default HTTP client - #1
Conversation
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]>
Reviewer's GuideThis PR switches tzdata’s default HTTP client from Hackney to Req, updates dependencies and minimum Elixir version to align with the Req/Finch/Mint stack, keeps a legacy Hackney adapter available only when explicitly configured, and adds focused unit and integration tests for the new Req adapter while cleaning up CI and a few docs/build details. Sequence diagram for Req HTTP client request flowsequenceDiagram
participant DataLoader as Tzdata_DataLoader
participant HTTPClient as Tzdata_HTTPClient_Req
participant ReqLib as Req
DataLoader->>HTTPClient: get(url, headers, options)
HTTPClient->>HTTPClient: request(:get, url, headers, options)
HTTPClient->>ReqLib: Req.request(req_options)
ReqLib-->>HTTPClient: {:ok, %Req.Response{status, body}}
HTTPClient-->>DataLoader: {:ok, {status, Req.get_headers_list(response), body}}
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've reviewed your changes and they look great!
Fixed security issues:
- hackney (link)
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Pull request overview
This PR switches tzdata’s default HTTP client from Hackney to Req (via Finch), keeping Hackney available only as an explicitly configured legacy adapter, and updates project docs/tests/CI to match the new baseline (Elixir 1.15+).
Changes:
- Add a new
Tzdata.HTTPClient.Reqadapter and set it as the default HTTP client in config andTzdata.DataLoader. - Make Hackney optional at runtime (no longer a production dependency) while preserving the existing adapter contract.
- Replace network-bound unit tests with deterministic
Req.Testcoverage and add opt-in integration tests; update CI/Travis and documentation accordingly.
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
lib/tzdata/http_client/req.ex |
Introduces the Req-based HTTP client adapter implementing the tzdata HTTP client behaviour. |
lib/tzdata/http_client/hackney.ex |
Keeps Hackney adapter available with a runtime presence check and updated guidance message. |
lib/tzdata/data_loader.ex |
Changes the default configured HTTP client to Req for download/metadata paths. |
mix.exs |
Raises minimum Elixir version and swaps prod dependency from Hackney to Req (adds Plug for tests). |
mix.lock |
Refreshes the dependency lockfile to reflect the Req/Finch/Mint/HPAX stack and removal of Hackney transitive deps. |
README.md |
Updates docs to reflect Req as the default HTTP client and documents legacy Hackney configuration. |
test/tzdata/http_client/req_test.exs |
Adds deterministic unit tests for the Req adapter (headers, redirects, errors). |
test/integration/req_download_test.exs |
Adds opt-in integration tests that hit IANA using the Req adapter. |
test/test_helper.exs |
Excludes :integration tests by default. |
lib/tzdata/tzdata_app.ex |
Minor refactor/formatting in application start child selection. |
lib/tzdata/parser.ex |
Removes an unused require to support warnings-as-errors. |
lib/tzdata.ex |
Updates a doctest expected value to match current bundled timezone data. |
.github/workflows/ci.yml |
Updates CI branch trigger and test matrix; removes older-Elixir job. |
.travis.yml |
Updates Travis Elixir/OTP versions to match the new supported baseline. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| defp http_client() do | ||
| Application.get_env(:tzdata, :http_client, Tzdata.HTTPClient.Hackney) | ||
| Application.get_env(:tzdata, :http_client, Tzdata.HTTPClient.Req) | ||
| end |
| content_length = | ||
| headers | ||
| |> Enum.find(fn {k, _v} -> String.downcase(k) == "content-length" end) | ||
| |> elem(1) | ||
| |> String.to_integer() |
| Tzdata requires a HTTP client in order to automatically update timezone | ||
| database. |
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
Tzdata.HTTPClient.Reqand makes it the default adapter.>= 0.6.1 and < 1.0.0, including SCHIP's locked Req 0.6.2, while excluding vulnerable Req 0.5 releases.Tzdata.Finchpool; Req supervises and uses its own pool.requirethat prevented warnings-as-errors compilation on the supported runtime.Req.Testcoverage; real IANA requests remain available as explicitly included integration tests.Validation
mix format --check-formattedon all changed Elixir files — passed.mix deps.unlock --check-unused— passed.mix test test/tzdata/http_client/req_test.exs— 7/7 passed with SCHIP's Req 0.6.2 and locked Req 0.7.3 (the adapter was also validated earlier against Req 0.5.17 before the vulnerable line was excluded).mix test test/integration/req_download_test.exs --include integration— 5/5 passed against IANA with SCHIP's Req 0.6.2 and locked Req 0.7.3.mix test --exclude doctest— 49 passed, 5 skipped, 22 excluded.mix deps.tree --only prod— Req/Finch production tree contains no Hackney.mix testunder local Elixir 1.20/OTP 29 — 66 passed, 5 skipped, 5 integration tests excluded. A stale Madrid transition value in an existing doctest was refreshed to match the bundled timezone data.mix compile --force --warnings-as-errors— passed after removing the pre-existing unusedrequire Tzdata.Util.Change Impact
Scope: diff
Files changed: 14
Functions changed: 8
Cross-app: no
Depth reached: 3
Test files identified: 2
Affected Areas
Tzdata.DataLoader.http_client/0changes the default adapter used by downloads, HEAD metadata, and file-size checks.Tzdata.HTTPClient.Req.get/3,head/3, andrequest/4provide the new default Req implementation.Tzdata.HTTPClient.Hackney.get/3,head/3, andensure_hackney!/0preserve Hackney as an explicitly configured legacy adapter with runtime dependency checks.Tzdata.ReleaseUpdaterandTzdata.DataBuilderare downstream automatic IANA polling, download, and release-loading paths.Tzdata.Mixfilechanges the dependency, configured adapter, and supported Elixir baseline.Tzdata.Parserhas a build-only cleanup of an unusedrequireso warnings-as-errors remains effective on Elixir 1.15+.Tzdata.periods/1documentation has its Madrid example synchronized with the bundled timezone data.test/tzdata/http_client/req_test.exsandtest/integration/req_download_test.exscover the new adapter.Category Hints
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
Tzdata.DataLoader,Tzdata.ReleaseUpdater, andTzdata.DataBuilder.Skills Used
opening-pull-requestsv1.1.0analyzing-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)Summary by Sourcery
Replace Hackney with Req as tzdata’s default HTTP client while preserving explicit Hackney compatibility and validating the new adapter against local and IANA requests.
New Features:
Bug Fixes:
Enhancements:
Build:
CI:
Documentation:
Tests:
Chores: