Copyright (c) 2009-2013 Erlang Solutions Ltd.
Some of the basic features provided by Lhttpc:
- HTTP basic auth
- SSL support
- Keepalive connections
- Pools for managing connections
- Support for IPv6
- Optional automatic cookie handling
- Chunked encoding
- Connection through proxy (only to HTTPS servers).
Download the sources or clone the git repository. Then you can build with:
make all
which will generate .beam files and documentation. To start the lhttpc OTP application, you first need to start the applications it depends on:
$ erl -pa ebin
1> application:start(crypto),
1> application:start(public_key),
1> application:start(ssl),
1> lhttpc:start().
ok
Lhttpc allows the user to send requests by always spawning a process for each of them, or reusing a single client process for several requests. It also allows the usage of pools of connections in order to keep connections alive and reuse them.
A single request without using a client process, will just spawn a process, do the request, and then stop the process.
Method = get,
URL = "http://www.erlang-solutions.com",
Headers = [],
Timeout = 100,
{ok,{{StatusCode, Status}, Headers, Body}} = lhttpc:request(URL, Method, Headers, Timeout).
Using the function request/9
it is also possible to specify the target server using Host
, Port
and Ssl
and a relative Path
. All the available options are listed in the documentation.
It is possible to first connect a client process to the target server, and then do a requests specifing just the relative Path:
{ok, Client} = lhttpc:connect_client("http://erlang-solutions.com", []),
{ok,{{StatusCode, Status}, Headers, Body}} = lhttpc:request_client(Client, "/", get, [], 100).
And then reuse the same client to do more requests to the same server.
Lhttpc supports pools of connections. They keep the connections to the different servers independently of the client processes. Therefore, if we do a requests specifing a pool, the client process will try to retrieve the connection from the pool if there is one, use it, and then return it to the pool before stopping. This makes it possible to share connections between different client processes and keep connections alive.
lhttpc:add_pool(my_pool),
lhttpc:request("http://www.erlang-solutions.com", get, [], [], 100, [{pool_options, [{pool, my_pool}]}]).
The lhttpc_manager
module provides functions to retrive information about the pools:
1>lhttpc_manager:connection_count(my_pool).
0
2>lhttpc:request("http://www.erlang-solutions.com", get, [], [], 100, [{pool_options, [{pool, my_pool}]}]).
3>lhttpc_manager:connection_count(my_pool).
1
4>lhttpc_manager:list_pools().
[{my_pool,[{max_pool_size,50},{timeout,300000}]}]
5>lhttpc_manager:update_connection_timeout(my_pool, 1000).
ok
6>lhttpc_manager:list_pools().
[{my_pool,[{max_pool_size,50},{timeout,1000}]}]
Lhttpc supports basic cookie handling. If you want the client process to automatically handle the cookies, use the option {use_cookies, true}
.
To be able to send the body divided in different parts, it is necessary to first start the client and then use the request_client
functions, since it is necessary to reuse the client for the calls to send_body_part
functions. You need to specify the {partial_upload, true}
option. Then use the send_body_part/2
and send_body_part/3
functions to send the body parts. http_eob
signals the end of the body. As an example:
{ok, Client} = lhttpc:connect_client("http://erlang-solutions.com", []),
lhttpc:request_client(Client, "/", get, [], [], 100, [{partial_upload, true}]),
lhttpc:send_body_part(Client, <<"some part of the body">>),
lhttpc:send_body_part(Client, <<"more body">>),
lhttpc:send_body_part(Client, http_eob).
It is also possible to download the body of the response by chunks. For this it is also necessary to connect the client first. The response body will be sent asynchronously to the specified process:
1> {ok, Client} = lhttpc:connect_client("http://www.google.com", []).
{ok,<0.40.0>}
2> lhttpc:request_client(Client, "/", get, [], [], 100, [{partial_download, [{window_size, infinity},{recv_proc, self()}]}]).
{ok,{{302,"Found"},
[{"Alternate-Protocol","80:quic"},
{"Server","GFE/2.0"},
{"Date","Wed, 30 Apr 2014 11:22:09 GMT"},
{"Content-Length","258"},
{"Location",
"http://www.google.co.uk/?gfe_rd=cr&ei=4dxgU_jHA8SK8QfWhQE"},
{"Content-Type","text/html; charset=UTF-8"},
{"Cache-Control","private"}],
partial_download}}
3> flush().
Shell got {body_part,<<"<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n<TITLE>302 Moved</TITLE></HEAD><BODY>\n<H1>302 Moved</H1>\nThe document has moved\n<A HREF=\"http://www.google.co.uk/?gfe_rd=cr&ei=4dxgU_jHA8SK8QfWhQE\">here</A>.\r\n</BODY></HTML>\r\n">>}
Shell got {http_eob,[]}
ok
You can control the flow by using the window_size
and the part_size
options:
4> lhttpc:request_client(Client, "/", get, [], [], 100, [{partial_download, [{window_size, 2}, {part_size, 100}, {recv_proc, self()}]}]).
{ok,{{302,"Found"},
[{"Alternate-Protocol","80:quic"},
{"Server","GFE/2.0"},
{"Date","Wed, 30 Apr 2014 11:23:51 GMT"},
{"Content-Length","258"},
{"Location",
"http://www.google.co.uk/?gfe_rd=cr&ei=R91gU4nwFcSK8QfWhQE"},
{"Content-Type","text/html; charset=UTF-8"},
{"Cache-Control","private"}],
partial_download}}
5> flush().
Shell got {body_part,<<"<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n<TITLE>302 Moved</TIT">>}
Shell got {body_part,<<"LE></HEAD><BODY>\n<H1>302 Moved</H1>\nThe document has moved\n<A HREF=\"http://www.google.co.uk/?gfe_rd=">>}
Shell got {body_part,window_finished}
ok
6> lhttpc:get_body_part(Client).
ok
7> flush().
Shell got {body_part,<<"cr&ei=R91gU4nwFcSK8QfWhQE\">here</A>.\r\n</BODY></HTML>\r\n">>}
Shell got {http_eob,[]}
ok