Skip to content
/ erqwest Public

An experimental erlang HTTP client wrapping reqwest

Notifications You must be signed in to change notification settings

dlesl/erqwest

Repository files navigation

erqwest

Hex pm

An erlang wrapper for reqwest using rustler. Map-based interface inspired by katipo.

Features

  • HTTP/1.1 and HTTP/2 with connection keepalive/reuse
  • Configurable SSL support, uses system root certificates by default
  • Sync and async interfaces
  • Proxy support
  • Optional cookies support
  • Optional gzip support

Prerequisites

  • Erlang/OTP
  • Rust
  • OpenSSL (not required on mac)

Or use the provided shell.nix if you have nix installed.

Usage

Start a client

ok = application:ensure_started(erqwest),
ok = erqwest:start_client(default).

This registers a client under the name default. The client maintains an internal connection pool.

Synchronous interface

No streaming

{ok, #{status := 200, body := Body}} = 
    erqwest:get(default, <<"https://httpbin.org/get">>),

{ok, #{status := 200, body := Body1}} =
    erqwest:post(default, <<"https://httpbin.org/post">>,
                 #{body => <<"data">>}).

Stream request body

{handle, H} = erqwest:post(default, <<"https://httpbin.org/post">>,
                           #{body => stream}),
ok = erqwest:send(H, <<"data, ">>),
ok = erqwest:send(H, <<"more data.">>),
{ok, #{body := Body}} = erqwest:finish_send(H).

Stream response body

{ok, #{body := Handle}} = erqwest:get(default, <<"https://httpbin.org/stream-bytes/1000">>,
                                      #{response_body => stream}),
ReadAll = fun Self() ->
              case erqwest:read(Handle, #{length => 0}) of
                {ok, Data} ->
                  [Data];
                {more, Data} ->
                  [Data|Self()]
              end
          end,
1000 = iolist_size(ReadAll()).

Conditionally consume response body

{ok, Resp} = erqwest:get(default, <<"https://httpbin.org/status/200,500">>,
                         #{response_body => stream}),
case Resp of
  #{status := 200, body := Handle} ->
    {ok, <<>>} = erqwest:read(Handle),
  #{status := BadStatus, body := Handle} ->
    %% ensures the connection is closed/can be reused immediately
    erqwest:cancel(Handle),
    io:format("Status is ~p, not interested~n", [BadStatus])
end.

Asynchronous interface

erqwest_async:req(default, self(), Ref=make_ref(), 
                  #{method => get, url => <<"https://httpbin.org/get">>}),
receive
    {erqwest_response, Ref, reply, #{body := Body}} -> Body
end.

See the docs for more details and and the test suite for more examples.