gmile
:httpc HTTP client is part of Erlang standard library, and as such can be easily used in Elixir code too. One particular advantage of using :httpc is that you can do a lot with it even with a minimal number of external dependencies. I prefer to use :httpc in one-off scripts and in production.
The :httpc reference is available at the Erlang standard library documentation website, here:
For now, I just wanted to publish an initial version of compilation of my own notes. Later I intent to update this post by adding notes to existing examples, adding references and even more examples. Feel free to modify the contents of the post too!
These examples are self-contained, can be copied to a script.exs file and executed via elixir script.exs command. Enjoy! ![]()
Index:
- sending a GET request returning a JSON payload
- sending a POST request with JSON request payload
- sending a POST request with no payload
- downloading a file
- uploading a file
- low-level tracing of HTTP interaction
General notes
-
for
:httpcto work,:inetsapp needs to be started beforehand; in case you intend to interact with HTTPS endpoints (which is what you want to do in most cases when making requests on public internet),:sslapp needs to be started as well; all examples bellow start both apps, -
all examples below pass a big-looking “parameter” called
http_request_opts- this is how you make sure:httpcperforms validation of the server’s TLS certificate; more on why this is necessary is described in “Erlang standard library: inets” page of “Secure Coding and Deployment Hardening Guidelines” by ERLef; without params undersslkey, you are likely to see the following warning on OTP < 26:[warning] Description: 'Authenticity is not established by certificate path validation' Reason: 'Option {verify, verify_peer} and cacertfile/cacerts is missing' -
:httpcappears to like char lists a lot; a common mistake I made in the beginning when using:httpcis feeding it with"https://example.com"a string, while it should be a a list of characters, e.g.~c"https://example.com"
Sending a GET request returning a JSON payload
Example
:inets.start()
:ssl.start()
url = ~s"https://httpbin.org/get"
headers = [{~s"accept", ~s"application/json"}]
http_request_opts = [
ssl: [
cacerts: :public_key.cacerts_get()
]
]
:httpc.request(:get, {url, headers}, http_request_opts, [])
Sending a POST request with JSON request payload
Example
:inets.start()
:ssl.start()
url = ~c"https://httpbin.org/post"
headers = []
content_type = ~c"application/json"
body = :json.encode(%{hello: "world"})
http_request_opts = [
ssl: [
cacerts: :public_key.cacerts_get()
]
]
:httpc.request(:post, {url, headers, content_type, body}, http_request_opts, [])
Sending a POST request with no payload
Example
:inets.start()
:ssl.start()
url = ~s"https://httpbin.org/post"
headers = []
content_type = ~c""
body = ~c""
http_request_opts = [
ssl: [
cacerts: :public_key.cacerts_get()
]
]
:httpc.request(:post, {url, headers, content_type, body}, http_request_opts, [])
Downloading a file
Example
:inets.start()
:ssl.start()
url = ~c"https://www.rfc-editor.org/rfc/pdfrfc/rfc1149.txt.pdf"
headers = []
path_to_file =
System.tmp_dir!()
|> Path.join("rfc1149.txt.pdf")
|> String.to_charlist()
http_request_opts = [
ssl: [
cacerts: :public_key.cacerts_get(),
customize_hostname_check: [
match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
]
]
]
{:ok, :saved_to_file} =
:httpc.request(:get, {url, headers}, http_request_opts, [stream: path_to_file])
Uploading a file
Example
Mix.install([:multipart])
:inets.start()
:ssl.start()
part = Multipart.Part.file_field("/tmp/rfc1149.txt.pdf", "my_file")
multipart =
Multipart.new()
|> Multipart.add_part(part)
content_length =
multipart
|> Multipart.content_length()
|> Integer.to_string()
|> String.to_charlist()
content_type =
multipart
|> Multipart.content_type("multipart/form-data")
|> String.to_charlist()
url = ~c"https://httpbin.org/anything"
headers = [{~c"Content-Length", content_length}]
payload = Multipart.body_binary(multipart)
http_request_opts = [
ssl: [
cacerts: :public_key.cacerts_get()
]
]
:httpc.request(:post, {url, headers, content_type, payload}, http_request_opts, [])
Notes
-
some HTTP clients offer convenient API around sending file payloads; UX varies on the library API design, and sometime can serve as a source of confusion (see this discussion, for instance),
-
because design of
:httpcappears to make little-to-no assumptions regarding the semantics of request payload, one can fairly say that:httpcsimply send bytes. As such, we should be able to “manually” construct any payload, including one to upload a file, and:httpcwill just handle it, -
multipartpackage, originally mentioned by its author @engineeringdept here focuses on just putting together a bunch of bytes that look like a correct “HTTP payload containing a file upload”,
Low-level tracing of HTTP interaction
Example
:inets.start()
:ssl.start()
url = ~c"https://httpbin.org/get"
headers = []
http_request_opts = [
ssl: [
cacerts: :public_key.cacerts_get()
]
]
:httpc.set_options([verbose: :debug])
:httpc.request(:get, {url, headers}, http_request_opts, [])
Notes
:debugoption value will easily generate several megabytes of information, while:tracevalue can potentially generate several tens of megabytes, so use it with care.
Trending in Wikis
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #elixirconf-eu
- #metaprogramming
- #hex










Showing Posts 13 to 4- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
gmile
Are you by any chance getting an error that looks like this too?
The error from screenshot is coming from VCR:
I’d suggest perhaps ExVCR, while serializing a request to cassette file, it stumbles upon writing SSL certificates, which are represented as binaries and cannot be dumped as mere strings. But this is a pure speculation, I am not sure how VCR works.
ExVCR also has this open issue, it contains a somewhat similar error: TLS 1.2 ssl doesn't work for hackney (httpoison) · Issue #105 · parroty/exvcr · GitHub
gmile
There’s GitHub - lexmag/oauther: OAuth 1.0 for Elixir · GitHub, though the library appears quite dated I won’t be surprised it it’s still works - I doubt the standard has changed significantly in the past years
Perhaps something else exists these days too, I am not sure.
Also, a shallow search on GitHub reveals various projects implement signing of request body themselves
Code search results · GitHub
soyjeansoy
@gmile this is a test file for posting page views in analytics (using exvcr):
ghannam80
Hello,
Do erlang have a library that can be used with :httpc to support request signing using RSA keys in line with OAUTH specs , in addition to request/response encryption and decryption.
A sample of what I am trying to do is available below but using other languages: GitHub - Mastercard/mastercard-api-client-tutorial: Generating and Configuring a Mastercard API Client · GitHub
Thanks in advance
gmile
gmile
@soyjeansoy no idea, off the top of my head
Could you show more of the code you have, preferably isolated to an elixir script (e.g. ending with .exs) where I could reproduce the problem? Also, which version of Elixir and Erlang are you using?
soyjeansoy
For some odd reason whenever I pass this opts (this is for
testenvironment), it threw an error:So this
http_request_opts, which is a list, is converted to string? If I’m not passing that opts (just empty list) it ran just fine.christhekeele
Just wanted to say, this helped me out a lot today.
Thanks!
BradS2S
Same here. I’ve been looking for something like this.
paulo-f-oliveira
There is a solution online, though it might not be immediately found if you’re searching for Elixir, and not Erlang: Erlang standard library: ssl | EEF Security WG. In the page body you’ll find
The Secure Coding and Deployment Hardening Guidelines, by the Erlang Ecosystem Foundation’s Security Working Group, is a great read, btw.