Skip to content

HTTP client for Erlang with support for HTTP/1 and HTTP/2 (Inspired by Mint)

License

Notifications You must be signed in to change notification settings

codeadict/gen_http

Repository files navigation

gen_http

CI codecov HTTP/2 Compliance RFC 7540 RFC 7541

A minimal, low-level HTTP client for Erlang.

HTTP/1.1 and HTTP/2 support. Pure Erlang. Zero dependencies.

Why?

Fast. Small API. Proper HTTP/1.1 and HTTP/2 support. Works with both protocols transparently.

Built to replace httpc with better performance and cleaner code.

HTTP/2 Compliance

156 tests covering RFC 7540 (HTTP/2) and RFC 7541 (HPACK). All frame types, stream states, flow control, priority handling, HPACK compression, and error conditions.

Tested against h2-client-test-harness. 100% pass rate.

Quick Start

%% HTTP/1.1
{ok, Conn} = gen_http:connect(http, "httpbin.org", 80),
{ok, Conn2, Ref} = gen_http:request(Conn, <<"GET">>, <<"/get">>, [], <<>>),

%% Collect response in active mode (default)
receive
    Msg ->
        case gen_http:stream(Conn2, Msg) of
            {ok, Conn3, [{status, Ref, 200}, {headers, Ref, Headers}, {data, Ref, Body}, {done, Ref}]} ->
                io:format("Body: ~s~n", [Body])
        end
end.

%% HTTP/2 (automatic via ALPN)
{ok, Conn} = gen_http:connect(https, "google.com", 443),
{ok, Conn2, Ref} = gen_http:request(Conn, <<"GET">>, <<"/">>, [], <<>>),
%% Same API, different protocol

Installation

Add to your rebar.config:

{deps, [
    {gen_http, {git, "https://github.com/codeadict/gen_http.git", {branch, "main"}}}
]}.

Examples

Simple GET Request

{ok, Conn} = gen_http:connect(http, "httpbin.org", 80),
{ok, Conn2, Ref} = gen_http:request(Conn, <<"GET">>, <<"/get">>, [], <<>>),

%% Active mode - receive messages
receive Msg ->
    {ok, Conn3, Responses} = gen_http:stream(Conn2, Msg),
    io:format("Responses: ~p~n", [Responses])
end.

POST with Body

{ok, Conn} = gen_http:connect(http, "httpbin.org", 80),

Headers = [{<<"content-type">>, <<"application/json">>}],
Body = <<"{\"hello\": \"world\"}">>,

{ok, Conn2, Ref} = gen_http:request(Conn, <<"POST">>, <<"/post">>, Headers, Body).

HTTPS with HTTP/2

%% ALPN automatically negotiates HTTP/2 if available
{ok, Conn} = gen_http:connect(https, "www.google.com", 443),
{ok, Conn2, Ref} = gen_http:request(Conn, <<"GET">>, <<"/">>, [], <<>>).

Passive Mode (Blocking)

{ok, Conn} = gen_http:connect(http, "httpbin.org", 80, #{mode => passive}),
{ok, Conn2, Ref} = gen_http:request(Conn, <<"GET">>, <<"/get">>, [], <<>>),

%% Blocking receive
{ok, Conn3, Responses} = gen_http:recv(Conn2, 0, 5000),
io:format("Responses: ~p~n", [Responses]).

Connection Reuse

{ok, Conn} = gen_http:connect(http, "httpbin.org", 80),

%% First request
{ok, Conn2, Ref1} = gen_http:request(Conn, <<"GET">>, <<"/get">>, [], <<>>),
%% ... handle response ...

%% Second request on same connection
{ok, Conn3, Ref2} = gen_http:request(Conn2, <<"GET">>, <<"/headers">>, [], <<>>),
%% ... handle response ...

{ok, _} = gen_http:close(Conn3).

Error Handling

case gen_http:connect(http, "example.com", 80) of
    {ok, Conn} ->
        case gen_http:request(Conn, <<"GET">>, <<"/">>, [], <<>>) of
            {ok, Conn2, Ref} ->
                handle_success(Conn2, Ref);
            {error, Conn2, Reason} ->
                %% Structured errors: {transport_error, _}, {protocol_error, _}, {application_error, _}
                case gen_http:is_retriable_error(Reason) of
                    true -> retry_request();
                    false -> handle_permanent_error(Reason)
                end
        end;
    {error, Reason} ->
        io:format("Connection failed: ~p~n", [Reason])
end.

Testing

SSL/H2/ALPN tests need nghttpx (from nghttp2). Install on macOS with brew install nghttp2. Suites that need it skip gracefully if it's not installed.

# Run all tests
rebar3 test

# Run specific test types
rebar3 eunit           # Unit tests
rebar3 ct              # Integration tests
rebar3 proper          # Property-based tests

# HTTP/2 compliance tests (excluded by default, requires separate Docker harness)
rebar3 ct --suite=h2_compliance_SUITE

Project Status

Early development. API may change.

Inspiration

  • Mint - HTTP client for Elixir
  • httpcore - Minimal HTTP client for Python
  • gun - HTTP client for Erlang

License

Apache 2.0

About

HTTP client for Erlang with support for HTTP/1 and HTTP/2 (Inspired by Mint)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages