Skip to content

Repository files navigation

hex.pm versionhex.pm downloadsAPI Docsunlicense

HTTPotion

HTTP client for Elixir, based on ibrowse. Continues the HTTPun tradition of HTTParty, HTTPretty, HTTParrot and HTTPie.

Note: unfortunately, ibrowse seems a bit buggy and not super actively maintained. You might want to check out Tesla with hackney instead, and this cert verification setup.

Installation

Add HTTPotion to your project's dependencies in mix.exs:

defpdepsdo[{:httpotion,"~> 3.1.0"}]enddefapplicationdo[applications: [:httpotion]]# Application dependency auto-starts it, otherwise: HTTPotion.startend

And fetch your project's dependencies:

$ mix deps.get

Usage

Note: You can load HTTPotion into the Elixir REPL by executing this command from the root of your project:

$ iex -S mix

Note: HTTPotion now enables certificate verification by default, using a few default CA bundle paths (/etc/ssl/cert.pem etc.) or the certifi package (which is not a mandatory dependency).

Some basic examples:

iex>response=HTTPotion.get"https://httpbin.org/get"%HTTPotion.Response{body: "{\n…",headers: %HTTPotion.Headers{hdrs: %{"connection"=>"keep-alive",}},status_code: 200}iex>HTTPotion.Response.success?(response)true# Response headers are wrapped to allow case-insensitive access (and to support both atoms and strings)iex>response.headers[:sErvEr]"meinheld/0.6.1"iex>response.headers["ConTenT-TyPe"]"application/json"# Response headers can have multiple valuesiex>response=HTTPotion.get"https://httpbin.org/response-headers?foo=1&foo=2&bar=1"%HTTPotion.Response{body: "{\n…",headers: %HTTPotion.Headers{hdrs: %{"foo"=>["1","2"],"bar"=>"1"}},status_code: 200}# You can provide a map for the query stringiex>HTTPotion.get("https://httpbin.org/get",query: %{page: 2})%HTTPotion.Response{body: "…",headers: ,status_code: 200}# Follow redirectsiex>HTTPotion.get("https://httpbin.org/redirect-to?url=http%3A%2F%2Fexample.com%2F",follow_redirects: true)%HTTPotion.Response{body: "…<title>Example Domain</title>…",headers: ,status_code: 200}# Send form dataiex>HTTPotion.post"https://httpbin.org/post",[body: "hello="<>URI.encode_www_form("w o r l d !!"),headers: ["User-Agent": "My App","Content-Type": "application/x-www-form-urlencoded"]]%HTTPotion.Response{body: "…",headers: ,status_code: 200}# Use a custom methodiex>HTTPotion.request:propfind,"http://httpbin.org/post",[body: "I have no idea what I'm doing"]%HTTPotion.Response{body: "…",headers: ,status_code: 405}# Send Basic auth credentialsiex>HTTPotion.get"https://httpbin.org/basic-auth/foo/bar",[basic_auth: {"foo","bar"}]%HTTPotion.Response{body: "…",headers: %HTTPotion.Headers{hdrs: %{"Access-Control-Allow-Credentials": "true",}},status_code: 200}# Pass options to ibrowse (note that it usually takes char_lists, not elixir strings)iex>HTTPotion.get"https://check-tls.akamaized.net",[ibrowse: [ssl_options: [versions,[:'tlsv1.1']]]]%HTTPotion.Response{body: "…TLS SNI: present - Check TLS - (https,tls1.1,ipv4)…",headers: ,status_code: 200}# Change the timeout (default is 5000 ms)iex>HTTPotion.get"https://example.com",[timeout: 10_000]# If there is an error a `HTTPotion.ErrorResponse` is returnediex>HTTPotion.get"http://localhost:1"%HTTPotion.ErrorResponse{message: "econnrefused"}# You can also raise `HTTPError` with the `bang` version of requestiex>HTTPotion.get!"http://localhost:1"**(HTTPotion.HTTPError)econnrefused

The Response is a struct, you can access its fields like: response.body.

response.headers is a struct (HTTPotion.Headers) that wraps a map to provide case-insensitive access (so you can use response.headers[:authorization] and it doesn't matter if the server returned AuThOrIZatIOn or something).

HTTPError is an exception that happens when a bang request (request! / get! / …) fails.

Available options and their default values:

{body: "",# Request's body contents, e.g. "{json: \"string\"}"headers: [],# Request's headers, e.g. [Accept: "application/json"]query: nil,# Query string, e.g. %{page: 1}timeout: 5000,# Timeout in milliseconds, e.g. 5000basic_auth: nil,# Basic auth credentials, e.g. {"username", "password"}stream_to: nil,# A process to stream the response to when performing async requestsdirect: nil,# An ibrowse worker for direct modeibrowse: [],# ibrowse optionsauto_sni: true,# Whether TLS SNI should be automatically configured (does URI parsing)follow_redirects: false,# Whether redirects should be followed}

Metaprogramming magic

You can extend HTTPotion.Base to make cool HTTP API wrappers (this example uses Poison for JSON):

defmoduleGitHubdouseHTTPotion.Basedefprocess_url(url)do"https://api.github.com/"<>urlenddefprocess_request_headers(headers)doDict.putheaders,:"User-Agent","github-potion"enddefprocess_response_body(body)dobody|>Poison.decode!endend
iex>GitHub.get("users/unrelentingtech").body["public_repos"]233

Read the source to see all the hooks.

Keep in mind that process_response_body and process_response_chunk get iodata. By default, they call IO.iodata_to_binary. But efficient parsers like Poison can work directly on iodata.

Asynchronous requests

You can get the response streamed to your current process asynchronously:

iex>HTTPotion.get"http://httpbin.org/get",[stream_to: self]%HTTPotion.AsyncResponse{id: -576460752303419903}iex>flush%HTTPotion.AsyncHeaders{id: -576460752303419903,status_code: 200,headers: %HTTPotion.Headers{hdrs: %{"connection"=>"keep-alive",}}}%HTTPotion.AsyncChunk{id: -576460752303419903,chunk: "{\n…"}%HTTPotion.AsyncEnd{id: -576460752303419903}

Note that instead of process_response_body, process_response_chunk is called on the chunks before sending them out to the receiver (the stream_to process).

Direct access to ibrowse workers

ibrowse allows you to use its separate worker processes directly. We expose this functionality through the direct option.

Don't forget that you have to pass the URL to the worker process, which means the worker only communicates with one server (domain!)

iex>{:ok,worker_pid}=HTTPotion.spawn_worker_process("http://httpbin.org")iex>HTTPotion.get"httpbin.org/get",[direct: worker_pid]%HTTPotion.Response{body: "…",headers: ["Connection": "close",],status_code: 200}

You can even combine it with async!

iex>{:ok,worker_pid}=HTTPotion.spawn_worker_process("http://httpbin.org")iex>HTTPotion.post"httpbin.org/post",[direct: worker_pid,stream_to: self,headers: ["User-Agent": "hello it's me"]]%HTTPotion.AsyncResponse{id: {1372,8757,656584}}

Type analysis

HTTPotion contains typespecs so your usage can be checked with dialyzer, probably via dialyxir or elixir-ls.

HTTPotion's tests are checked with dialyxir.

Contributing

Please feel free to submit pull requests!

By participating in this project you agree to follow the Contributor Code of Conduct.

License

This is free and unencumbered software released into the public domain.
For more information, please refer to the UNLICENSE file or unlicense.org.

About

[Deprecated because ibrowse is not maintained] HTTP client for Elixir (use Tesla please)

Topics

Resources

Code of conduct

Stars

720 stars

Watchers

13 watching

Forks

Used by

Contributors

Languages