Skip to content

Conversation

@lessless
Copy link

@lessless lessless commented Dec 27, 2025

This PR adds pluggable HTTP client support to allow users to swap Erlang's built-in httpc for alternative HTTP clients.

Problem

httpc sends Content-Length: 0 header for GET requests, which AWS ELB rejects. This causes Oidcc.ProviderConfiguration.Worker.start_link/1 to fail when connecting to OIDC providers hosted behind it and potentially other load balancers.

{:ok, pid} =Oidcc.ProviderConfiguration.Worker.start_link(%{issuer: "https://identity.moneyhub.co.uk/oidc",name: PocketTax.MoneyHubOidccConfigProvider,})
[error] GenServer PocketTax.MoneyHubOidccConfigProvider terminating
** (stop) {:configuration_load_failed, {:http_error, 400, "<html>\r\n<head><title>400 Bad Request</title></head>\r\n<body>\r\n<center><h1>400 Bad Request</h1></center>\r\n</body>\r\n</html>\r\n"}}
Last message: {:continue, :load_configuration}
State: {:state, :undefined, :undefined, "https://identity.moneyhub.co.uk/oidc", %{}, :undefined, :undefined, PocketTax.MoneyHubOidccConfigProvider, 1000, 30000, :stop, :undefined}
** (EXIT from #PID<0.413.0>) shell process exited with reason: {:configuration_load_failed, {:http_error, 400, "<html>\r\n<head><title>400 Bad Request</title></head>\r\n<body>\r\n<center><h1>400 Bad Request</h1></center>\r\n</body>\r\n</html>\r\n"}}

Solution

Introduce an HTTP client behaviour:

  • oidcc_http_client - Erlang behaviour with request/2 callback
  • oidcc_http_client_httpc - Default adapter wrapping httpc (preserves backwards compatibility)
  • Oidcc.HttpClient.Req - Optional Req adapter for Elixir projects (only compiles when req is a dependency)

Configuration

Per-request:

Oidcc.ProviderConfiguration.Worker.start_link(%{
  issuer: "https://example.com",
  provider_configuration_opts: %{
    request_opts: %{http_client: Oidcc.HttpClient.Req}
  }
})

Application-wide:

config :oidcc, http_client: Oidcc.HttpClient.Req

Backwards Compatibility

  • Default behaviour unchanged (httpc)
  • Existing timeout, ssl, httpc_profile options continue to work
  • No breaking changes for existing users

Add an HTTP client behaviour abstraction that allows users to swap
httpc for alternative clients (Req, Hackney, Finch, etc.).

Erlang's httpc sends Content-Length: 0 header for GET requests,
which some load balancers (e.g., AWS ELB) reject.

- New `oidcc_http_client` behaviour with `request/2` callback
- Default `oidcc_http_client_httpc` adapter (no breaking changes)
- Optional `Oidcc.HttpClient.Req` adapter for Elixir projects
- Configuration via request_opts or application env
@lessless lessless force-pushed the feature/pluggable-http-client branch from 54d4e0f to e779693 Compare December 27, 2025 22:06
@maennchen
Copy link
Member

Thanks for the PR!

I would have really liked to talk about this before opening a change like this. This kind of workaround adds quite a bit of complexity on our side, and in this case I don’t think that complexity is worth it.

I’d strongly prefer to fix the underlying issue in OTP instead of carrying the workaround here. To that end, I’ve opened an issue upstream:
erlang/otp#10513

That keeps oidcc simpler and avoids us having to maintain OTP-specific edge cases long-term.

Happy to discuss next steps or coordinate on the OTP fix if you’re up for it.

@maennchen maennchen closed this Jan 5, 2026
@lessless
Copy link
Author

lessless commented Jan 5, 2026

Hey @maennchen ,

No worries at all. And thanks for looking into this. I appreciate the issue you opened in the OTP repo and agree it's rational to solve it upstream.

So far I decided to stick with my own implementation because it's not that large in size and I'm still trying to take the project of the ground, so every hour matters.

Happy New Year!

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.

2 participants