All Projects β†’ seriyps β†’ xhttpc

seriyps / xhttpc

Licence: Apache-2.0 license
Extensible HTTP Client for Erlang

Programming Languages

erlang
1774 projects

Projects that are alternatives of or similar to xhttpc

resto
πŸ”— a CLI app can send pretty HTTP & API requests with TUI
Stars: ✭ 113 (+352%)
Mutual labels:  http-client
restofus
Restofus - a cross-platform (REST) API client.
Stars: ✭ 18 (-28%)
Mutual labels:  http-client
cute-http
δΈ€δΈͺ基于axiosε°θ£…ηš„ζ›΄ζ˜“η”¨ηš„http库。
Stars: ✭ 18 (-28%)
Mutual labels:  http-client
NClient
πŸ’« NClient is an automatic type-safe .Net HTTP client that allows you to call web service API methods using annotated interfaces or controllers without boilerplate code.
Stars: ✭ 25 (+0%)
Mutual labels:  http-client
relay
Relay lets you write HTTP requests as easy to read, structured YAML and dispatch them easily using a CLI. Similar to tools like Postman
Stars: ✭ 22 (-12%)
Mutual labels:  http-client
waspy
WASP framework for Python
Stars: ✭ 43 (+72%)
Mutual labels:  http-client
jetty-reactive-httpclient
Jetty ReactiveStreams HttpClient
Stars: ✭ 63 (+152%)
Mutual labels:  http-client
http4s-good-practices
Collection of what I consider good practices in Http4s (WIP)
Stars: ✭ 74 (+196%)
Mutual labels:  http-client
framework
Aplus Full-Stack Framework
Stars: ✭ 172 (+588%)
Mutual labels:  http-client
theon
Declarative library to build Web API clients & SDKs for the browser and node.js
Stars: ✭ 50 (+100%)
Mutual labels:  http-client
http
Tiny, embeddable HTTP client with simple API for the browser
Stars: ✭ 21 (-16%)
Mutual labels:  http-client
lhs
βš›οΈ REST services accelerator: Rails gem providing an easy, active-record-like interface for http (hypermedia) json services
Stars: ✭ 130 (+420%)
Mutual labels:  http-client
EasyRetrofit
an encapsulation for Retrofit2.1
Stars: ✭ 34 (+36%)
Mutual labels:  http-client
rawhttp
HTTP library to make it easy to deal with raw HTTP.
Stars: ✭ 156 (+524%)
Mutual labels:  http-client
BCA-Phantom
A multi-platform HTTP(S) Reverse Shell Server and Client in Python 3
Stars: ✭ 80 (+220%)
Mutual labels:  http-client
angular6-httpclient-example
Angular 6 HttpClient: Consume RESTful API Example
Stars: ✭ 38 (+52%)
Mutual labels:  http-client
requests-rs
Rust HTTP client library styled after awesome Python requests
Stars: ✭ 37 (+48%)
Mutual labels:  http-client
moleculer-http-client
HTTP client mixin that allows Moleculer services to communicate with remote REST APIs
Stars: ✭ 14 (-44%)
Mutual labels:  http-client
Rx.Http
A reactive way to make HTTP Request in .NET Core πŸš€
Stars: ✭ 62 (+148%)
Mutual labels:  http-client
restler
Restler is a beautiful and powerful Android app for quickly testing REST API anywhere and anytime.
Stars: ✭ 120 (+380%)
Mutual labels:  http-client

xhttpc - eXtensible HTTP(S) Client for Erlang

Easily extensible - just add your own middlewares. Simple tiny core - all the additional functionality splitted to middleware modules. No extra processes and message passing. Not a http client, but http client wrapper - built on top of lhttpc HTTP client, support for other HTTP clients (httpc, hackney, ibrowse etc) can be added easily. Batteries included - see below.

Inspired mostly by python's urllib2. Battle-tested in production system.

API

%% Start underlying HTTP client library
% if you use OTP's httpc
inets:start().
% if you use lhttpc (recommended)
[application:start(App) || App <- [crypto, public_key, ssl, lhttpc]].

%% Initialize session (passing list of middleware names or pairs {middleware_name, init_args})
Session = xhttpc:init(
    [compression_middleware,
     cookie_middleware,
     {defaults_middleware, [{append_hdrs, [{"User-Agent", "xhttpc 0.0.1"}]},
                            {default_options, [{timeout, 10}]}]}]),

%% Perform HTTP request, using positional arguments
{S2, {ok, {{200, StatusLine}, Headers, Body}}} =
    xhttpc:request(Session, "http://example.com/",
                   post, [{"Content-Type", "application/x-www-form-urlencoded"}],
                   <<"a=b">>, [{timeout, 10000}]),

%% Perform HTTP request, using `#request{}` record API
-include_lib("xhttpc/include/xhttpc.hrl").
{S3, {ok, {{200, StatusLine}, Headers, Body}}} =
    xhttpc:request(S2, #request{url="http://example.com/"}),

%% Perform HTTP request, using proplists API
{S4, {ok, {{200, StatusLine}, Headers, Body}}} =
    xhttpc:request(S3,
                   "http://example.com/",
                   [{method, post},
                    {body, "a=b"},
                    {headers, [{"Content-Type", "application/x-www-form-urlencoded"}]}]),

%% Interact with middleware's state
{S5, Cookies} = xhttpc:call(S4, cookie_middleware, {get_cookies, "example.com", "/"}).

%% Terminate session
ok = xhttpc:terminate(S5).

Api sequence diagram:

 API call                      | compression_mw |     | defaults_mw   |     | lhttpc/httpc/etc |
-------------------------------+----------------+-----+---------------+-----+------------------+
xhttpc:request/2           --> |  request/3     | --> |  request/3    | --> | lhttpc:request/5 |
                               |                |     |               |     |       VVV        |
response()                 <-- |  response/4    | <-- |  response/4   | <-- |     response()   |

xhttpc:call(defaults_mw, Arg)<-:----------------:---> |  call/2       |     |                  |

xhttpc:(init|terminate)    --> | init|terminate |     |               |     |                  |
                           \---:----------------:---> | init|terminate|     |                  |

Try rebar doc to generate HTML API documentation from sources.

FAQ

Q: I think API is too verbose / I don't like records in API.

A: Just make your own wrappers / shortcuts.

Q: I don't like this session-chains S1, S2, S3....

A: You can store sessions in a process / gen_server / public ETS or any storage you like, eg:

% this will spawn new gen_server and store session in it.
Pid = my_wrapper:init([...]).
% following calls will lookup / update session from storage using `Pid` as identifier.
{ok, {{200, StatusLine}, Headers, Body}} = my_wrapper:get(Pid, "http://example.com/").
Result = my_wrapper:call(Pid, ...).
ok = my_wrapper:terminate(Pid).

If you'll create shared storage for sessions (like public ETS table), you can also use your worker proces's self() pid as session ID and don't pass Pid parameter at all.

Batteries included

  • Traffic compression (compression_middleware)
  • Redirects, with loop detection (redirect_middleware)
  • Cookies (cookie_middleware + RFC6265 cookie parser / serializer)
  • Automatic http referer (referer_middleware)
  • Constant default / additional headers and options for each request (defaults_middleware)
  • TODO: basic HTTP auth middleware

TODO

  • Support for partial upload actually supported, but just need to create some wrappers
  • Support for partial download eg, for compression middleware
  • More examples: ETS cookie storage API with external session storage (to avoid S1, S2, S3, SN problem)
  • More http client adapters:
    • DONE httpc
    • hackney
    • ibrowse

How to make your own middleware

As an example, let's implement middleware, named 'logging_middleware'. It will log some user-defined uniq session ID, request method, url, response code and run-time to error_logger. Middleware must implement xhttpc_middleware behaviour (see xhttpc_middleware.erl).

This include following methods:

  • init/1 - will be called when user initialize session by call xhttpc:init/1.
  • request/3 - before request will be sended to server.
  • response/4 - before reply will be returned to user.
  • terminate/2 - when user terminates session by call xhttpc:terminate/2, and
  • call/2 - if user wish to call concrete middleware like xhttpc:call(logging_middleware, Args).

So, first we add standard module header:

-module(logging_middleware).
-behaviour(xhttpc_middleware).  % declare behaviour

-export([init/1, request/3, response/4, terminate/2, call/2]).
-include_lib("xhttpc/include/xhttpc.hrl").  % #request{} definition there.

-record(state, {session_id, timer_start, n_requests=0}).  % middleware's internal state

Now, let's implement some callback functions:

init([SessionID]) ->
  {ok, #state{session_id=SessionID}}.

So, when user create new session S = xhttpc:init([{logging_middleware, ["MySessionID"]}])., this function will be called and it return {ok, #state{session_id="MySessionID"}}. We return #state{} just like gen_server's state - it will be passed to each API function call as last argument later.

request(Session, Request, #state{n_requests=NReqs} = State) ->
    Begin = os:timestamp(),
    {update, Session, Request, State#state{timer_start=Begin,
                                           n_requests=NReqs + 1}}.

Now, when user call xhttpc:request/2,6, request first goes to each middlewares request/3 callback. We only capture current timestamp before request been executed (sended to the server) and increment requests counter.

response(Session, #xhttpc_request{url=Url, method=Method}, Response,
         #state{timer_start=Start, n_requests=NReqs, session_id=SID} = State) ->
    Runtime = timer:now_diff(os:timestamp(), Start) / 1000000,
    Status = case Response of
        {ok, {{Code, _}, _Hdrs, _Body}} ->
            Code;
        {error, Reason} ->
            Reason
    end,
    % lager:info(...)
    error_logger:info_msg("Request #~p of session ~p; Method: ~p Url: ~s runtime: ~p status: ~p",
                          [NReqs, SID, Method, Url, Runtime, Status]),
    noaction.

After request has been executed and response been returned by underlying HTTP client, middleware's response/4 will be called (in reverse order). We calculate request's runtime as diff of saved in State request-start-time and current timestamp. Next, if response was successful - extract HTTP response code, if not - error reason. And finally, log all the data to error_logger. Note, that we return atom noaction as a flag that Session, State and Response hasn't been modified by this callback.

terminate(Reason, #state{session_id=SID}) ->
    error_logger:info_msg("Session ~p has been terminated with reason ~p", [SID, Reason]),
    ok.

No comments. Just callback for xhttpc:terminate/1,2.

Now imagine, that we need to get number of currently executed requests with that session. That's why we have call/2 method here. First, we may implement user-friendly API method for it:

-export([get_requests_count/1]).

get_requests_count(Session) ->
    xhttpc:call(Session, ?MODULE, get_requests_count).

This is just a wrapper around xhttpc:call/3. Next, we implement a callback

call(get_requests_count, #state{n_requests=NReqs} = State) ->
    {NReqs, State}.

So simple! Isn't it? Note that we return 2-tuple from call/2 - that way we can not only retrieve some data from middleware's state, but can also mutate it's state (for example, we can change it's session_id or reset requests counter on the fly).

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].