• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

lib/H02-Aug-2017-551432

test/H02-Aug-2017-425339

.gitignoreH A D02-Aug-201767 109

.travis.ymlH A D02-Aug-2017104 1110

CHANGELOG.mdH A D02-Aug-20173.8 KiB15694

LICENSEH A D02-Aug-20171 KiB2117

README.mdH A D02-Aug-20179.7 KiB253186

mix.exsH A D03-May-2022865 3933

README.md

1![](http://i.imgur.com/WwqN8JO.png)
2# HTTPoison [![Build Status](https://travis-ci.org/edgurgel/httpoison.svg?branch=master)](https://travis-ci.org/edgurgel/httpoison) [![Hex pm](http://img.shields.io/hexpm/v/httpoison.svg?style=flat)](https://hex.pm/packages/httpoison) [![hex.pm downloads](https://img.shields.io/hexpm/dt/httpoison.svg?style=flat)](https://hex.pm/packages/httpoison)
3
4HTTP client for Elixir, based on
5[HTTPotion](https://github.com/myfreeweb/httpotion)
6([documentation](http://hexdocs.pm/httpoison/)).
7
8## Note about broken ssl in Erlang 19
9Until this [issue](https://bugs.erlang.org/browse/ERL-192) is fixed ssl handshakes may fail. If you receive this error:
10```
11{:error, %HTTPoison.Error{id: nil, reason: :closed}}
12```
13Try the following fix:
14```elixir
15HTTPoison.get("https://example.com/", [], [ ssl: [{:versions, [:'tlsv1.2']}] ])
16```
17
18## But... why something so similar to HTTPotion?
19
20HTTPoison uses [hackney](https://github.com/benoitc/hackney) to execute HTTP requests instead of ibrowse. I like hackney :thumbsup:
21
22Using hackney we work only with binaries instead of string lists.
23
24## Installation
25
26First, add HTTPoison to your `mix.exs` dependencies:
27
28```elixir
29def deps do
30  [{:httpoison, "~> 0.12"}]
31end
32```
33
34and run `$ mix deps.get`. Add `:httpoison` to your applications list if your Elixir version is 1.3 or lower:
35
36```elixir
37def application do
38  [applications: [:httpoison]]
39end
40```
41
42## Usage
43
44```elixir
45iex> HTTPoison.start
46iex> HTTPoison.get! "http://httparrot.herokuapp.com/get"
47%HTTPoison.Response{
48  body: "{\n  \"args\": {},\n  \"headers\": {} ...",
49  headers: [{"Connection", "keep-alive"}, {"Server", "Cowboy"},
50  {"Date", "Sat, 06 Jun 2015 03:52:13 GMT"}, {"Content-Length", "495"},
51  {"Content-Type", "application/json"}, {"Via", "1.1 vegur"}],
52  status_code: 200
53}
54iex> HTTPoison.get! "http://localhost:1"
55** (HTTPoison.Error) :econnrefused
56iex> HTTPoison.get "http://localhost:1"
57{:error, %HTTPoison.Error{id: nil, reason: :econnrefused}}
58
59iex> HTTPoison.post "http://httparrot.herokuapp.com/post", "{\"body\": \"test\"}", [{"Content-Type", "application/json"}]
60{:ok, %HTTPoison.Response{body: "{\n  \"args\": {},\n  \"headers\": {\n    \"host\": \"httparrot.herokuapp.com\",\n    \"connection\": \"close\",\n    \"accept\": \"application/json\",\n    \"content-type\": \"application/json\",\n    \"user-agent\": \"hackney/1.6.1\",\n    \"x-request-id\": \"4b85de44-6227-4480-b506-e3b9b4f0318a\",\n    \"x-forwarded-for\": \"76.174.231.199\",\n    \"x-forwarded-proto\": \"http\",\n    \"x-forwarded-port\": \"80\",\n    \"via\": \"1.1 vegur\",\n    \"connect-time\": \"1\",\n    \"x-request-start\": \"1475945832992\",\n    \"total-route-time\": \"0\",\n    \"content-length\": \"16\"\n  },\n  \"url\": \"http://httparrot.herokuapp.com/post\",\n  \"origin\": \"10.180.37.142\",\n  \"form\": {},\n  \"data\": \"{\\\"body\\\": \\\"test\\\"}\",\n  \"json\": {\n    \"body\": \"test\"\n  }\n}",
61    headers: [{"Connection", "keep-alive"}, {"Server", "Cowboy"},
62    {"Date", "Sat, 08 Oct 2016 16:57:12 GMT"}, {"Content-Length", "681"},
63    {"Content-Type", "application/json"}, {"Via", "1.1 vegur"}],
64status_code: 200}}
65```
66
67You can also easily pattern match on the `HTTPoison.Response` struct:
68
69```elixir
70case HTTPoison.get(url) do
71  {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
72    IO.puts body
73  {:ok, %HTTPoison.Response{status_code: 404}} ->
74    IO.puts "Not found :("
75  {:error, %HTTPoison.Error{reason: reason}} ->
76    IO.inspect reason
77end
78```
79### Options
80
81There are a number of supported options(*not to be confused with the HTTP options method*), documented [here](https://hexdocs.pm/httpoison/HTTPoison.html#request/5), that can be added to your request. The example below shows the use of the `:ssl` and `:recv_timeout` options for a post request to an api that requires a bearer token. The `:ssl` option allows you to set options accepted by th [Erlang SSL module](http://erlang.org/doc/man/ssl.html), and `:recv_timeout` sets a timeout on receiving a response, the default is 5000ms.
82
83```elixir
84token = "some_token_from_another_request"
85url = "https://example.com/api/endpoint_that_needs_a_bearer_token"
86headers = ["Authorization": "Bearer #{token}", "Accept": "Application/json; Charset=utf-8"]
87options = [ssl: [{:versions, [:'tlsv1.2']}], recv_timeout: 500]
88{:ok, response} = HTTPoison.get(url, headers, options)
89```
90
91And the example below shows the use of the `:ssl` options for a post request to an api that requires a client certification.
92
93```elixir
94url = "https://example.org/api/endpoint_that_needs_client_cert"
95options = [ssl: [certfile: "certs/client.crt"]]
96{:ok, response} = HTTPoison.post(url, [], options)
97```
98
99### Wrapping `HTTPoison.Base`
100
101You can also use the `HTTPoison.Base` module in your modules in order to make
102cool API clients or something. The following example wraps `HTTPoison.Base` in
103order to build a client for the GitHub API
104([Poison](https://github.com/devinus/poison) is used for JSON decoding):
105
106```elixir
107defmodule GitHub do
108  use HTTPoison.Base
109
110  @expected_fields ~w(
111    login id avatar_url gravatar_id url html_url followers_url
112    following_url gists_url starred_url subscriptions_url
113    organizations_url repos_url events_url received_events_url type
114    site_admin name company blog location email hireable bio
115    public_repos public_gists followers following created_at updated_at
116  )
117
118  def process_url(url) do
119    "https://api.github.com" <> url
120  end
121
122  def process_response_body(body) do
123    body
124    |> Poison.decode!
125    |> Map.take(@expected_fields)
126    |> Enum.map(fn({k, v}) -> {String.to_atom(k), v} end)
127  end
128end
129```
130
131```elixir
132iex> GitHub.start
133iex> GitHub.get!("/users/myfreeweb").body[:public_repos]
13437
135```
136
137It's possible to extend the functions listed below:
138
139```elixir
140defp process_request_body(body), do: body
141
142defp process_response_body(body), do: body
143
144defp process_request_headers(headers) when is_map(headers) do
145  Enum.into(headers, [])
146end
147
148defp process_request_headers(headers), do: headers
149
150defp process_request_options(options), do: options
151
152defp process_response_chunk(chunk), do: chunk
153
154defp process_headers(headers), do: headers
155
156defp process_status_code(status_code), do: status_code
157
158defp process_url(url), do: url
159```
160
161### Async requests
162
163HTTPoison now comes with async requests!
164
165```elixir
166iex> HTTPoison.get! "https://github.com/", %{}, stream_to: self
167%HTTPoison.AsyncResponse{id: #Reference<0.0.0.1654>}
168iex> flush
169%HTTPoison.AsyncStatus{code: 200, id: #Reference<0.0.0.1654>}
170%HTTPoison.AsyncHeaders{headers: %{"Connection" => "keep-alive", ...}, id: #Reference<0.0.0.1654>}
171%HTTPoison.AsyncChunk{chunk: "<!DOCTYPE html>...", id: #Reference<0.0.0.1654>}
172%HTTPoison.AsyncEnd{id: #Reference<0.0.0.1654>}
173:ok
174```
175
176### Cookies
177
178HTTPoison allows you to send cookies:
179
180```elixir
181iex> HTTPoison.get!("http://httparrot.herokuapp.com/cookies", %{}, hackney: [cookie: ["session=a933ec1dd923b874e691; logged_in=true"]])
182%HTTPoison.Response{body: "{\n  \"cookies\": {\n    \"session\": \"a933ec1dd923b874e691\",\n    \"logged_in\": \"true\"\n  }\n}",
183 headers: [{"Connection", "keep-alive"}, ...],
184 status_code: 200}
185```
186
187You can also receive cookies from the server by reading the `"set-cookie"` headers in the response:
188
189```elixir
190iex(1)> response = HTTPoison.get!("http://httparrot.herokuapp.com/cookies/set?foo=1")
191iex(2)> cookies = Enum.filter(response.headers, fn
192...(2)> {"Set-Cookie", _} -> true
193...(2)> _ -> false
194...(2)> end)
195[{"Set-Cookie", "foo=1; Version=1; Path=/"}]
196```
197
198You can see more usage examples in the test files (located in the
199[`test/`](test)) directory.
200
201### Connection Pools
202
203Normally **hackney** [opens and closes connections on demand](https://github.com/benoitc/hackney#reuse-a-connection), but it also creates a [default pool](https://github.com/benoitc/hackney#use-the-default-pool) of connections which are reused for requests to the same host. If the connection and host support keepalive, the connection is kept open until explicitly closed.
204
205To use the default pool, you can just declare it as an option:
206
207```elixir
208HTTPoison.get("httpbin.org/get", [], hackney: [pool: :default])
209```
210
211It is possible to use different pools for different purposes when a more fine grained allocation of resources is necessary.
212
213#### Simple pool declaration
214
215The easiest way is to just pass the name of the pool, and hackney will create it if it doesn't exist. Pools are independent from each other (they won't compete for connections) and are created with the default configuration.
216
217```elixir
218HTTPoison.get("httpbin.org/get", [], hackney: [pool: :first_pool])
219HTTPoison.get("httpbin.org/get", [], hackney: [pool: :second_pool])
220```
221
222#### Explicit pool creation
223
224If you want to use different configuration options you can create a pool manually [when your app starts](http://elixir-lang.org/getting-started/mix-otp/supervisor-and-application.html#the-application-callback) with `:hackney_pool.start_pool/2`.
225
226```elixir
227:ok = :hackney_pool.start_pool(:first_pool, [timeout: 15000, max_connections: 100])
228```
229
230From the already linked [hackney's readme](https://github.com/benoitc/hackney#use-the-default-pool):
231
232> `timeout` is the time we keep the connection alive in the pool, `max_connections` is the number of connections maintained in the pool. Each connection in a pool is monitored and closed connections are removed automatically.
233
234#### Pools as supervised processes
235
236A third option is to add the pool as part of your supervision tree:
237
238```elixir
239children = [
240  :hackney_pool.child_spec(:first_pool, [timeout: 15000, max_connections: 100])
241]
242```
243
244Add that to the application supervisor and `first_pool` will be available to be used by HTTPoison/hackney.
245
246
247## License
248
249    Copyright © 2013-2014 Eduardo Gurgel <eduardo@gurgel.me>
250
251    This work is free. You can redistribute it and/or modify it under the
252    terms of the MIT License. See the LICENSE file for more details.
253