Packages
hackney
2.0.0
4.7.2
4.7.1
4.7.0
4.6.1
4.6.0
4.5.2
4.5.1
4.5.0
4.4.5
4.4.3
4.4.2
4.4.1
4.4.0
4.3.0
4.2.3
4.2.2
4.2.1
4.2.0
4.1.0
4.0.3
4.0.2
4.0.1
4.0.0
3.2.1
3.2.0
3.1.2
3.1.1
3.1.0
3.0.3
3.0.2
3.0.1
3.0.0
retired
2.0.1
2.0.0
2.0.0-beta.1
1.25.0
1.24.1
1.24.0
1.23.0
1.22.0
1.21.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.2
1.18.1
1.18.0
1.17.4
1.17.3
1.17.2
1.17.1
1.17.0
1.16.0
1.15.2
1.15.1
1.15.0
1.14.3
1.14.2
1.14.0
1.13.0
1.12.1
1.12.0
1.11.0
1.10.1
1.10.0
1.9.0
1.8.6
1.8.5
1.8.4
1.8.3
1.8.2
1.8.0
1.7.1
1.7.0
1.6.6
retired
1.6.5
1.6.4
retired
1.6.3
1.6.2
1.6.1
1.6.0
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.10
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.0
1.0.6
1.0.5
1.0.2
1.0.1
0.15.2
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.1
Simple HTTP client with HTTP/1.1, HTTP/2, and HTTP/3 support
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
guides/http_guide.md
# HTTP Guide
This guide covers hackney's HTTP features in depth.
## Request Anatomy
```erlang
hackney:request(Method, URL, Headers, Body, Options) ->
{ok, StatusCode, RespHeaders, Ref} | {error, Reason}
```
## Request Bodies
### Binary Body
```erlang
hackney:post(URL,
[{<<"content-type">>, <<"application/json">>}],
<<"{\"key\": \"value\"}">>
).
```
### Form-Encoded Body
```erlang
hackney:post(URL, [], {form, [{<<"key">>, <<"value">>}]}).
```
### Multipart Body
Multipart requests are used to upload files and send form data together.
#### Basic File Upload
```erlang
hackney:post(URL, [], {multipart, [
{<<"field">>, <<"value">>},
{file, <<"/path/to/file.txt">>}
]}).
```
#### File Upload with Custom Field Name
Use `{file, Path, FieldName, ExtraHeaders}` to specify the form field name:
```erlang
%% Upload file to "attachment" field instead of default "file"
hackney:post(URL, [], {multipart, [
{file, <<"/path/to/document.pdf">>, <<"attachment">>, []}
]}).
```
#### File Upload with Full Control
For complete control over the Content-Disposition header:
```erlang
Path = <<"/path/to/photo.jpg">>,
FName = hackney_bstr:to_binary(filename:basename(Path)),
Disposition = {<<"form-data">>,
[{<<"name">>, <<"photo">>},
{<<"filename">>, FName}]},
hackney:post(URL, [], {multipart, [
{file, Path, Disposition, []}
]}).
```
#### Mixed File and Text Fields
Combine file uploads with text fields:
```erlang
hackney:post(URL, [], {multipart, [
{file, <<"/path/to/image.jpg">>, <<"image">>, []},
{<<"title">>, <<"My Photo">>},
{<<"description">>, <<"A nice picture">>}
]}).
```
#### Text Fields with Explicit Content-Type
Some servers require explicit content-type for text fields:
```erlang
hackney:post(URL, [], {multipart, [
{file, <<"/path/to/doc.pdf">>, <<"document">>, []},
{<<"name">>, <<"Report">>, [{<<"content-type">>, <<"text/plain">>}]}
]}).
```
#### Supported Part Formats
| Format | Description |
|--------|-------------|
| `{file, Path}` | File with auto-generated field name |
| `{file, Path, ExtraHeaders}` | File with extra headers |
| `{file, Path, FieldName, ExtraHeaders}` | File with custom field name |
| `{file, Path, {Disposition, Params}, ExtraHeaders}` | Full control |
| `{Name, Data}` | Text field (Data must be binary) |
| `{Name, Data, ExtraHeaders}` | Text field with headers |
| `{Name, Data, Disposition, ExtraHeaders}` | Text field with full control |
### Streaming Body
```erlang
{ok, Ref} = hackney:post(URL, Headers, stream),
ok = hackney:send_body(Ref, <<"chunk1">>),
ok = hackney:send_body(Ref, <<"chunk2">>),
ok = hackney:finish_send_body(Ref),
{ok, Status, RespHeaders, Ref} = hackney:start_response(Ref).
```
## Response Handling
### Read Full Body
```erlang
{ok, 200, Headers, Ref} = hackney:get(URL),
{ok, Body} = hackney:body(Ref).
```
### Automatic Decompression
Hackney can automatically decompress gzip and deflate encoded responses:
```erlang
{ok, 200, Headers, Body} = hackney:get(URL, [], <<>>, [
with_body,
{auto_decompress, true}
]).
```
When `auto_decompress` is enabled:
- Adds `Accept-Encoding: gzip, deflate` header to requests
- Automatically decompresses the response body based on `Content-Encoding`
- Supports gzip, deflate, and x-gzip encodings
- Non-compressed responses are returned unchanged
### Stream Response Body
```erlang
{ok, 200, Headers, Ref} = hackney:get(URL),
stream_body(Ref).
stream_body(Ref) ->
case hackney:stream_body(Ref) of
{ok, Data} -> process(Data), stream_body(Ref);
done -> ok
end.
```
## HTTP/2 Support
Hackney automatically negotiates HTTP/2 for HTTPS connections via ALPN.
### Automatic HTTP/2
```erlang
%% HTTP/2 used automatically when server supports it
{ok, 200, Headers, Body} = hackney:get(
<<"https://nghttp2.org/">>,
[],
<<>>,
[with_body]
).
```
### Force Protocol
```erlang
%% HTTP/2 only
hackney:get(URL, [], <<>>, [{protocols, [http2]}]).
%% HTTP/1.1 only
hackney:get(URL, [], <<>>, [{protocols, [http1]}]).
```
### Detect Protocol
HTTP/2 responses have lowercase header names:
```erlang
case hd(Headers) of
{<<"date">>, _} -> http2;
{<<"Date">>, _} -> http1
end.
```
For details on multiplexing, server push, and architecture, see the [HTTP/2 Guide](http2_guide.md).
## Async Responses
```erlang
{ok, Ref} = hackney:get(URL, [], <<>>, [async]),
receive
{hackney_response, Ref, {status, Status, _}} -> ok
end,
receive
{hackney_response, Ref, {headers, Headers}} -> ok
end,
receive
{hackney_response, Ref, done} -> ok;
{hackney_response, Ref, Bin} -> ok
end.
```
### Async Once
```erlang
{ok, Ref} = hackney:get(URL, [], <<>>, [{async, once}]),
receive {hackney_response, Ref, Msg} -> ok end,
hackney:stream_next(Ref). %% Request next message
```
### Stream to Another Process
Use `stream_to` to send async messages to a different process:
```erlang
Receiver = spawn(fun() -> receive_loop() end),
{ok, Ref} = hackney:get(URL, [], <<>>, [
async,
{stream_to, Receiver}
]).
```
When `stream_to` is specified:
- The connection is owned by the `stream_to` process, not the caller
- If `stream_to` dies, the connection terminates
- If the original caller dies, the connection continues as long as `stream_to` is alive
- This ensures proper cleanup when the message recipient terminates
## Connection Pooling
### Default Pool
```erlang
hackney:get(URL). %% Uses default pool
```
### Named Pools
```erlang
hackney_pool:start_pool(my_api, [
{max_connections, 100},
{timeout, 150000}
]),
hackney:get(URL, [], <<>>, [{pool, my_api}]).
```
## Redirects
```erlang
{ok, 200, Headers, Ref} = hackney:get(URL, [], <<>>, [
{follow_redirect, true},
{max_redirect, 5}
]).
```
## Proxies
### HTTP Proxy
```erlang
hackney:get(URL, [], <<>>, [
{proxy, <<"http://proxy:8080">>}
]).
```
### Environment Variables
hackney reads `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY` automatically.
## SSL/TLS
### Custom CA Certificate
```erlang
hackney:get(URL, [], <<>>, [
{ssl_options, [{cacertfile, "/path/to/ca.crt"}]}
]).
```
### Skip Verification
```erlang
hackney:get(URL, [], <<>>, [insecure]).
```
## Timeouts
```erlang
hackney:get(URL, [], <<>>, [
{connect_timeout, 5000},
{recv_timeout, 30000}
]).
```
## Cookies
```erlang
hackney:get(URL, [], <<>>, [{cookie, <<"session=abc">>}]).
%% Parse response cookies
{ok, 200, Headers, _} = hackney:get(URL),
Cookies = hackney:cookies(Headers).
```
## Basic Authentication
```erlang
hackney:get(URL, [], <<>>, [
{basic_auth, {<<"user">>, <<"pass">>}}
]).
```
## Next Steps
- [HTTP/2 Guide](http2_guide.md) - Multiplexing, server push, architecture
- [WebSocket Guide](websocket_guide.md)
- [Migration Guide](MIGRATION.md)