Little (LFE) HTTP Client – A light-weight LFE wrapper around lhttpc
This library is meant to be a dead-simple HTTP client for LFE projects. That’s all there is to it. It uses lhttpc under the hood, which can be a little cumbersome for those new to Erlang when used directly; as such, lhc will likely provide a welcome alternative.
Just add it to your
rebar.configdeps:
{deps, [
...
{lhc, ".*",
{git, "git@github.com:lfex/lhc.git", "master"}}
]}.
Then execute the usual
rebarcommand:
$ rebar compile
See notes to the right.
Each of the API functions that map to an HTTP verb have at least one arity
that supports setting lhc options which are passed to lhc:request.
These options may have zero or more of the following:
return - return type. Can be one of body (default), status,
headers or allcallback - the function that gets called once a result is obtained
from the lhttpc client libraryThe lhc examples below are run against a YAWS REST server demo store. You can download and run the “store” yourself when you clone the repo.
For POSTing data, we use the ljson LFE
library. To make this available to your project, simply add it to your
rebar.config file, per the instructions on the ljson project README.
startIt is required to start lhc before using the API:
> (lhc:start)
(#(inets ok) #(ssl ok) #(lhttpc ok) #(lhc ok))
If you attempt to use the API without having first started it, you will see errors like the following:
> (lhc:get "http://localhost:8000/demos/store3/orders")
#(error
#(exit
#(noproc
#(gen_server call
(undefined #(socket <0.35.0> "google.com" 80 false) infinity)))
(#(lhttpc_manager ensure_call 6
(#(file "src/lhttpc_manager.erl") #(line 234)))
#(lhttpc_client execute 9 (#(file "src/lhttpc_client.erl") #(line 158)))
#(lhttpc_client request 9 (#(file "src/lhttpc_client.erl") #(line 99)))
#(proc_lib init_p_do_apply 3 (#(file "proc_lib.erl") #(line 239))))))
This function simply starts the lhc LFE HTTP client. This is required for any use of the lhc client library.
getPerform a simple
GET:
> (lhc:get "http://localhost:8000/demos/store3/orders")
"{\"result\": \"You got a list of orders.\"}"
>
lhc:get/1 - takes a URLlhc:get/2 - takes a URL and lhc optionslhc:get/3 - takes a URL, list of headers, and lhc optionsDepending upon the return option (default being body) and callback
option, each of these will return a parsed result iof the content obtained by
lhttpc.
headTo just get the headers from the server:
> (lhc:head "http://localhost:8000/demos/store3/")
(#("Content-Type" "application/json")
#("Date" "Thu, 27 Aug 2015 14:39:31 GMT")
#("Server" "Yaws 2.0"))
>
lhc:head/1 - takes a URLlhc:head/2 - takes a URL and lhc optionslhc:head/3 - takes a URL, list of headers, and lhc optionsReturns just the parsed headers of the result from lhttpc. By defualt, this
is a list of tuples, each being a key/value pair of header name and head value.
postTo
POST, we first create a payload. The demo REST API we’re testing against can take any string value, but many applications will expect JSON data. Let’s use JSON here:
> (set payload (ljson:encode '(#(make #"Volvo") #(model #"P1800"))))
#"{"make":"Volvo","model":"P1800"}"
>
With our payload in hand, we can now
POSTto create a new order:
> (lhc:post "http://localhost:8000/demos/store3/order" payload)
"{\"result\": \"{\"order-id\": 124}\"}"
>
lhc:post/1 - takes a URL, making an empty data POSTlhc:post/2 - takes a URL and POST datalhc:post/3 - takes a URL, POST data, and lhc optionslhc:post/4 - takes a URL, POST data, a list of headers, and lhc
optionsputTo
PUT, we first create a payload similar to what we did forPOSTbut with the modified data we want:
> (set payload (ljson:encode '(#(make #"Volvo") #(model #"2015 P1800"))))
#"{"make":"Volvo","model":"2015 P1800"}"
>
We can now
PUTto update our order:
> (lhc:put "http://localhost:8000/demos/store3/order/124" payload)
"{\"result\": \"You updated all of order 124.\"}"
>
lhc:put/1 - takes a URL, making an empty data PUTlhc:put/2 - takes a URL and PUT datalhc:put/3 - takes a URL, PUT data, and lhc optionslhc:put/4 - takes a URL, PUT data, a list of headers, and lhc
optionsdelete
DELETEing is as simple as passing the appropriate URL:
> (lhc:delete "http://localhost:8000/demos/store3/order/124")
"{\"result\": \"You deleted order 124.\"}"
>
lhc:delete/1 - takes a URLlhc:delete/2 - takes a URL and lhc optionslhc:delete/3 - takes a URL, list of headers, and lhc optionstraceNote that TRACE isn’t implemented in YAWS so we don’t have example code to
share. Likewise, lhc:trace is untested against a server implementing
TRACE – please
let us know
if you run into usage problems with this function.
lhc:trace/1 - takes a URLlhc:trace/2 - takes a URL and datalhc:trace/3 - takes a URL, data, and lhc optionslhc:trace/4 - takes a URL, data, a list of headers, and lhc optionsoptionslhc provides support for requesting the allowed methods for a given URL:
> (lhc:options "http://localhost:8000/demos/store3/order/124")
"{\"result\": \"You got the allowed method for order/124: GET, PUT, POST, DELETE, and OPTIONS.\"}"
lhc:options/1 - takes a URLlhc:options/2 - takes a URL and lhc optionslhc:options/3 - takes a URL, list of headers, and lhc optionsconnectTBD
(Note that this HTTP verb is not supported by YAWS, so we’ll likely save it for very last.)
patchLet’s define the partial data we want to send:
> (set payload (ljson:encode '(#(model #"2015 P1800"))))
#"{"model":"2015 P1800"}"
>
We can now
PATCHour order, updating only the part that we wanted to change:
> (lhc:patch "http://localhost:8000/demos/store3/order/124" payload)
"{\"result\": \"You updated part of order 124.\"}"
>
PATCH is useful for large data sets where you only one a part of the data
updated. You can use PATCH to update just the bits you’re interested in,
without having to send the entire payload like is recommended with PUT.
lhc:patch/1 - takes a URL, making an empty data PATCHlhc:patch/2 - takes a URL and PATCH datalhc:patch/3 - takes a URL, PATCH data, and lhc optionslhc:patch/4 - takes a URL, PATCH data, a list of headers, and lhc
optionsrequest[Code ready, need docs]
parse-results[Code ready, need docs]
[Code ready, need docs]
Basic usage from Erlang is straight-forward:
1> lhc:start().
[{inets,ok},{ssl,ok},{lhttpc,ok},{lhc,ok}]
2> lhc:get("http://localhost:8000/demos/store3/orders").
"{\"result\": \"You got a list of orders.\"}"
3> Payload = ljson:encode([{make,<<"Volvo">>},{model,<<"P1800">>}]).
<<"{\"make\":\"Volvo\",\"model\":\"P1800\"}">>
4> lhc:post("http://localhost:8000/demos/store3/order", Payload).
"{\"result\": \"{\"order-id\": 124}\"}"
Using lhc from Erlang is very straight-forward; there aren’t even hypens in most module or function names, so no need to escape any atoms!
Why would you want to, you ask? Well, it might be convenient to use a library that offers consistent usage patterns (and function calls) across a selection of HTTP clients in the Erlang ecosystem. You and your developers could write your HTTP code once, and then change backend clients as the needs arose, without having to change any code (just some small configuration settings).
Take a look at the sample usage to the right to get a sense of using lhc from Erlang.
lhc supports a handfule of backends, with more future ones planned.
Current backends are:
Planned backends currently include the following:
Edit the backend value in the
lhcsection of yourlfe.configfile. See the lhclfe.configfor an example of this. Once edited, runningstartwill use that value:
> (lhc:start)
(#(inets ok) #(ssl ok) #(lhttpc ok) #(lhc ok))
> (lhc:get-backend)
lhttpc
>
You can also start lhc with your preferred backend:
> (lhc:start 'httpc)
(#(inets ok) #(ssl ok) #(lhc ok))
>
You can change backends at any time:
> (lhc:change-backend 'lhttpc)
(#(backend #(previous httpc) #(current lhttpc)))
>
Bakends my be selected one of three ways:
backend value in the lhc section of the lfe.config
fileFind out which backend you are using:
> (lhc:get-backend)
lhttpc
>
Find out which backend is configured in your
lfe.configfile:
> (lhc:get-backend-cfg)
lhttpc
Find out which module holds the backend functions:
> (lhc:get-backend-module)
lhc-backend
Several convenience functions are provided in support of lhc backends.
See examples to the right.
The lhc user agent string:
"LFE Little HTTP Client/0.1.0 (LFE 0.10.0-dev; Erlang 18; backend=lhttpc) (+http://github.com/lfex/lhc)"
The lhc user agent will show up in any HTTP server log files where client user agent strings are recorded. To the right is an example of the lhc user agent string.
Documentation is available for all previous releases:
Copyright © 2014-2015 Duncan McGreggor oubiwann@gmail.com
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.