134 changes: 100 additions & 34 deletions doc/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@


#hackney - simple HTTP client in Erlang#
# hackney - HTTP client library in Erlang #

Copyright (c) 2012-2013 Benoît Chesneau.

Copyright (c) 2012 Benoît Chesneau.

__Version:__ 0.4
__Version:__ 0.4.1

# hackney

**hackney** is a simple HTTP client.
**hackney** is an HTTP client library for Erlan.

Main features:

Expand All @@ -19,7 +18,9 @@ Main features:
- SSL support
- Keepalive handling
- basic authentication
- stream the response
- stream the response and the requests
- multipart support (stramed or not)
- chunked encoding support
- Can send files using the sendfile API
- Chunked encoding support
- Optionnal socket pool
Expand All @@ -40,10 +41,14 @@ files and documentation.
To run tests run 'make test'.
To generate doc, run 'make doc'.

Or add it to your rebar config<pre>{deps, [
Or add it to your rebar config

```
{deps, [
....
{hackney, ".*", {git, "git://github.com/benoitc/hackney.git", {branch, "master"}}}
]}.</pre>
]}.
```

## Basic usage

Expand All @@ -56,55 +61,90 @@ hackney is an
application. You have to start it first before using all the functions.
The hackney applications will start for you the default socket pool.

To start in the console run :<pre>$ erl -pa ebin
To start in the console run :

```
$ erl -pa ebin
1>> hackney:start().
ok</pre>
ok
```

It will start hackney and all the application it depends:<pre>application:start(crypto),
It will start hackney and all the application it depends:

```
application:start(crypto),
application:start(public_key),
application:start(ssl),
application:start(hackney).</pre>
application:start(hackney).
```

Or add hackney to the applications member of your relase an app

### Simple request without pool

Do a simple a requet that will return a client state:<pre>Method = get,
Do a simple a requet that will return a client state:

```
Method = get,
URL = <<"https://friendpaste.com">>,
Headers = [],
Payload = <<>>,
Options = [],
{ok, StatusCode, RespHeaders, Client} = hackney:request(Method, URL,
Headers, Payload,
Options).</pre>
Options).
```

The request method return the tupple `{ok, StatusCode, Headers, Client}`
or `{error, Reason}`.

If you enable the **parse_transform**, you can also do:<pre>hackney:get(URL, Headers, Payload, Options)</pre>
If you enable the **parse_transform**, you can also do:

```
hackney:get(URL, Headers, Payload, Options)
```

To enable parse transform add the following option to the erlang
compiler flags:<pre>{parse_transform, hackney_transform}</pre>
compiler flags:

```
{parse_transform, hackney_transform}
```

Alternately, you can add it to the module you wish to compile:

```
-compile([{parse_transform, hackney_transform}]).
```

Alternately, you can add it to the module you wish to compile:<pre>-compile([{parse_transform, hackney_transform}]).</pre>
### Read the body

### Read the body<pre>{ok, Body, Client1} = hackney:body(Client).</pre>
```
{ok, Body, Client1} = hackney:body(Client).
```

`hackney:body/1` fetch the body. To fetch it by chunk you can use the
`hackney:stream/body/1` function:<pre>read_body(MaxLength, Client, Acc) when MaxLength > byte_size(Acc) ->
`hackney:stream/body/1` function:

```
read_body(MaxLength, Client, Acc) when MaxLength > byte_size(Acc) ->
case stream_body(Client) of
{ok, Data, Client2} ->
read_body(MaxLength, Client2, << Acc/binary, Data/binary >>);
{done, Client2} ->
{ok, Acc, Client2};
{error, Reason} ->
{error, Reason}
end.</pre>
end.
```

### Reuse the client object

If your connection support the keepalive you can reuse the Client
record using the `hackney:send_request/2` function:<pre>ReqBody = << "{
record using the `hackney:send_request/2` function:

```
ReqBody = << "{
\"id\": \"some_paste_id\",
\"rev\": \"some_revision_id\",
\"changeset\": \"changeset in unidiff format\"
Expand All @@ -115,7 +155,8 @@ NextMethod = post,
NextReq = {NextMethod, NextPath, ReqHeaders, ReqBody}
{ok, _, _, Client2} = hackney:send_request(Client1, NextReq).
{ok, Body1, Client3} = hackney:body(Client2),
hackney:close(Client3).</pre>
hackney:close(Client3).
```

Here we are posting a JSON paylod to '/' on the service friendpaste to
create a paste. Then we close the client connection.
Expand Down Expand Up @@ -146,7 +187,10 @@ function `hackney:stream_request_body/2` to stream the request body and
The function `hackney:start_response/1` is waiting a Client with
theresponse state equal to the atom `waiting`.

ex:<pre>ReqBody = << "{
ex:

```
ReqBody = << "{
\"id\": \"some_paste_id2\",
\"rev\": \"some_revision_id\",
\"changeset\": \"changeset in unidiff format\"
Expand All @@ -160,34 +204,47 @@ Method = post,
{ok, Client1} = hackney:stream_request_body(ReqBody, Client),
{ok, _Status, _Headers, Client2} = hackney:start_response(Client1),
{ok, Body, Client3} = hackney:body(Client2),
hackney:close(Client3).</pre>
hackney:close(Client3).
```

### Use a pool

To reuse a connection globally in your application you can also use a
socket pool. On startup, hackney launch a pool named default. To use it
do the following:<pre>Method = get,
do the following:

```
Method = get,
URL = <<"https://friendpaste.com">>,
Headers = [],
Payload = <<>>,
Options = [{pool, default}],
{ok, StatusCode, RespHeaders, Client} = hackney:request(Method, URL, Headers,
Payload, Options).</pre>
Payload, Options).
```

By adding the tuple `{pool, default}` to the options, hackney will use
the connections stored in that pool.

You can also use different pools in your application which will allows
you to maintain some kind of group of connections.<pre>PoolName = mypool,
you to maintain some kind of group of connections.

```
PoolName = mypool,
Options = [{timeout, 150000}, {pool_size, 100}],
{ok, Pid} = hackney:start_pool(PoolName, Options),</pre>
{ok, Pid} = hackney:start_pool(PoolName, Options),
```

`timeout` is the time we keep alive the conneciton in the pool,
`pool_size` is the number of connections maintained in the pool. Each
connection in a pool is monitored and closed connections are removed
automatically.

To close a pool do:<pre>hackney:stop_pool(PoolName).</pre>
To close a pool do:

```
hackney:stop_pool(PoolName).
```

> Note: Sometimes you want to always use the default pool in your app
> without having to set the client option each time. You can now do this
Expand All @@ -208,14 +265,18 @@ only follow 303 redirection (see other) if the method is a POST.

Last Location is stored in the client state in the `location` property.

ex:<pre>Method = get,
ex:

```
Method = get,
URL = "http://friendpaste.com/",
ReqHeaders = [{<<"accept-encoding">>, <<"identity">>}],
ReqBody = <<>>,
Options = [{follow_redirect, true}, {max_redirect, true}],
{ok, S, H, Client} = hackney:request(Method, URL, ReqHeaders,
ReqBody, Options),
{ok, Body, Client1} = hackney:body(Client).</pre>
{ok, Body, Client1} = hackney:body(Client).
```

### Proxy a connection

Expand All @@ -233,10 +294,15 @@ issue](http://github.com/benoitc/hackney/issues).

If you want to contribute patches or improve the doc, you will need to
build hackney using the `rebar_dev.config` file. It can also be built
using the **Makefile**:<pre>$ make dev ; # compile & get deps
$ make devclean ; # clean all files</pre>
using the **Makefile**:

```
$ make dev ; # compile & get deps
$ make devclean ; # clean all files
```


##Modules##
## Modules ##


<table width="100%" border="0" summary="list of modules">
Expand Down
1 change: 1 addition & 0 deletions doc/edoc-info
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
%% encoding: UTF-8
{application,hackney}.
{packages,[]}.
{modules,[hackney,hackney_app,hackney_deps,hackney_form,hackney_headers,
Expand Down
209 changes: 136 additions & 73 deletions doc/hackney.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@


#Module hackney#
# Module hackney #
* [Function Index](#index)
* [Function Details](#functions)


<a name="index"></a>

##Function Index##
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#body-1">body/1</a></td><td>Return the full body sent with the response.</td></tr><tr><td valign="top"><a href="#body-2">body/2</a></td><td>Return the full body sent with the response as long as the body
Expand All @@ -16,6 +16,7 @@ Possible value are :
<ul>
<li><code>eof</code>: end the multipart request</li>
<li><code>{Id, {File, FileName}}</code>: to stream a file</li>
%% <li><code>{Id, {File, FileName, FileOptions}}</code>: to stream a file</li>
<li><code>{data, {start, Id, DileName, ContentType}}</code>: to start to stream
arbitrary binary content</li>
<li><code>{data, Bin}`: send a binary. Use it only after emitting a
Expand All @@ -26,123 +27,156 @@ the multipart request</li>
boundary</li>
<li><code>{Id, Value}</code>: send an arbitrary value as a boundary. Filename and
Id are identique</li>
</ul>
File options can be:
<ul>
<li><code>{offset, Offset}</code>: start to send file from this offset</li>
<li><code>{bytes, Bytes}</code>: number of bytes to send</li>
<li><code>{chunk_size, ChunkSize}</code>: the size of the chunk to send</li>
</ul></td></tr><tr><td valign="top"><a href="#stream_request_body-2">stream_request_body/2</a></td><td>stream the request body.</td></tr></table>


<a name="functions"></a>

##Function Details##
## Function Details ##

<a name="body-1"></a>

###body/1##
### body/1 ###


<pre><code>
body(Client::#client{}) -&gt; {ok, binary(), #client{}} | {error, atom()}
</code></pre>

<pre>body(Client::#client{}) -&gt; {ok, binary(), #client{}} | {error, atom()}</pre>
<br></br>


Return the full body sent with the response.<a name="body-2"></a>
Return the full body sent with the response.
<a name="body-2"></a>

### body/2 ###

###body/2##

<pre><code>
body(MaxLength::non_neg_integer() | infinity, Client::#client{}) -&gt; {ok, binary(), #client{}} | {error, atom()}
</code></pre>

<pre>body(MaxLength::non_neg_integer() | infinity, Client::#client{}) -&gt; {ok, binary(), #client{}} | {error, atom()}</pre>
<br></br>


Return the full body sent with the response as long as the body
length doesn't go over MaxLength.<a name="close-1"></a>

###close/1##
length doesn't go over MaxLength.
<a name="close-1"></a>

### close/1 ###

`close(Client) -> any()`

close the client<a name="connect-1"></a>

###connect/1##
close the client
<a name="connect-1"></a>

### connect/1 ###

`connect(Client) -> any()`

connect a socket and create a client state.<a name="connect-3"></a>

###connect/3##
connect a socket and create a client state.
<a name="connect-3"></a>

### connect/3 ###

`connect(Transport, Host, Port) -> any()`

<a name="connect-4"></a>

###connect/4##
<a name="connect-4"></a>

### connect/4 ###

`connect(Transport, Host, Port, Client) -> any()`

<a name="end_stream_request_body-1"></a>

###end_stream_request_body/1##
<a name="end_stream_request_body-1"></a>

### end_stream_request_body/1 ###

`end_stream_request_body(Client) -> any()`

end streaming the request body.<a name="pool-1"></a>

###pool/1##
end streaming the request body.
<a name="pool-1"></a>

### pool/1 ###

`pool(Client) -> any()`

get current pool pid or name used by a client if needed<a name="request-1"></a>
get current pool pid or name used by a client if needed
<a name="request-1"></a>

### request/1 ###

###request/1##

<pre><code>
request(URL::binary() | list()) -&gt; {ok, integer(), list(), #client{}} | {error, term()}
</code></pre>

<pre>request(URL::binary() | list()) -&gt; {ok, integer(), list(), #client{}} | {error, term()}</pre>
<br></br>


make a request<a name="request-2"></a>
make a request
<a name="request-2"></a>

### request/2 ###

###request/2##

<pre><code>
request(Method::term(), URL::binary() | list()) -&gt; {ok, integer(), list(), #client{}} | {error, term()}
</code></pre>

<pre>request(Method::term(), URL::binary() | list()) -&gt; {ok, integer(), list(), #client{}} | {error, term()}</pre>
<br></br>


make a request<a name="request-3"></a>
make a request
<a name="request-3"></a>

### request/3 ###

###request/3##

<pre><code>
request(Method::term(), URL::binary() | list(), Headers::list()) -&gt; {ok, integer(), list(), #client{}} | {error, term()}
</code></pre>

<pre>request(Method::term(), URL::binary() | list(), Headers::list()) -&gt; {ok, integer(), list(), #client{}} | {error, term()}</pre>
<br></br>


make a request<a name="request-4"></a>
make a request
<a name="request-4"></a>

### request/4 ###

###request/4##

<pre><code>
request(Method::term(), URL::binary() | list(), Headers::list(), Body::term()) -&gt; {ok, integer(), list(), #client{}} | {error, term()}
</code></pre>

<pre>request(Method::term(), URL::binary() | list(), Headers::list(), Body::term()) -&gt; {ok, integer(), list(), #client{}} | {error, term()}</pre>
<br></br>


make a request<a name="request-5"></a>
make a request
<a name="request-5"></a>

###request/5##
### request/5 ###


<pre>request(Method::term(), Hackney_url::binary(), Headers::list(), Body::term(), Options0::list()) -&gt; {ok, integer(), list(), #client{}} | {error, term()}</pre>
<br></br>
<pre><code>
request(Method::term(), Hackney_url::binary(), Headers::list(), Body::term(), Options0::list()) -&gt; {ok, integer(), list(), #client{}} | {error, term()}
</code></pre>

<br></br>



make a request


Args:

* <strong>Method</strong>
Expand Down Expand Up @@ -197,13 +231,18 @@ redirection even on POST
* insecure: to perform "insecure" SSL connections and
transfers without checking the certificate

* {connect_timeout, infinity | integer()}: timeout used when
estabilishing a connection, in milliseconds. Default is 8000

* {recv_timeout, infinity | integer()}: timeout used when
receiving a connection. Default is infinity



* `proxy_options()`: options to connect by a proxy:


* `proxy_options()`: options to connect by a proxy:


* binary(): url to use for the proxy. Used for basic HTTP
proxy
Expand All @@ -213,79 +252,88 @@ for HTTP proxy



<a name="send_request-2"></a>

###send_request/2##

<a name="send_request-2"></a>

`send_request(Client, Req) -> any()`
### send_request/2 ###

send a request using the current client state<a name="set_sockopts-2"></a>
`send_request(Client, Req) -> any()`

###set_sockopts/2##
send a request using the current client state
<a name="set_sockopts-2"></a>

### set_sockopts/2 ###

`set_sockopts(Client, Options) -> any()`

add set sockets options in the client<a name="skip_body-1"></a>
add set sockets options in the client
<a name="skip_body-1"></a>

###skip_body/1##
### skip_body/1 ###


<pre>skip_body(Client::#client{}) -&gt; {ok, #client{}} | {error, atom()}</pre>
<br></br>
<pre><code>
skip_body(Client::#client{}) -&gt; {ok, #client{}} | {error, atom()}
</code></pre>

<br></br>

skip the full body. (read all the body if needed).<a name="start-0"></a>

###start/0##
skip the full body. (read all the body if needed).
<a name="start-0"></a>

### start/0 ###

`start() -> any()`

Start the couchbeam process. Useful when testing using the shell.<a name="start_pool-2"></a>

###start_pool/2##
Start the couchbeam process. Useful when testing using the shell.
<a name="start_pool-2"></a>

### start_pool/2 ###

`start_pool(Name, Options) -> any()`

start a pool<a name="start_response-1"></a>
start a pool
<a name="start_response-1"></a>

###start_response/1##
### start_response/1 ###


<pre>start_response(Client::#client{}) -&gt; {ok, integer(), list(), #client{}} | {error, term()}</pre>
<pre><code>
start_response(Client::#client{}) -&gt; {ok, integer(), list(), #client{}} | {error, term()}
</code></pre>

<br></br>


start a response.
Useful if you stream the body by yourself. It will fetch the status
and headers of the response. and return<a name="stop-0"></a>

###stop/0##
and headers of the response. and return
<a name="stop-0"></a>

### stop/0 ###

`stop() -> any()`

Stop the couchbeam process. Useful when testing using the shell.<a name="stop_pool-1"></a>

###stop_pool/1##
Stop the couchbeam process. Useful when testing using the shell.
<a name="stop_pool-1"></a>

### stop_pool/1 ###

`stop_pool(Name) -> any()`

stop a pool<a name="stream_body-1"></a>

###stream_body/1##
stop a pool
<a name="stream_body-1"></a>

### stream_body/1 ###

`stream_body(Client) -> any()`

Stream the response body.<a name="stream_multipart_request-2"></a>

###stream_multipart_request/2##
Stream the response body.
<a name="stream_multipart_request-2"></a>

### stream_multipart_request/2 ###

`stream_multipart_request(Body, Client) -> any()`

Expand All @@ -296,6 +344,8 @@ Possible value are :

* `{Id, {File, FileName}}`: to stream a file

%% * `{Id, {File, FileName, FileOptions}}`: to stream a file

* `{data, {start, Id, DileName, ContentType}}`: to start to stream
arbitrary binary content

Expand All @@ -310,14 +360,27 @@ boundary
* `{Id, Value}`: send an arbitrary value as a boundary. Filename and
Id are identique


File options can be:

* `{offset, Offset}`: start to send file from this offset

* `{bytes, Bytes}`: number of bytes to send

* `{chunk_size, ChunkSize}`: the size of the chunk to send


<a name="stream_request_body-2"></a>

###stream_request_body/2##
### stream_request_body/2 ###


<pre><code>
stream_request_body(Body::term(), Client::#client{}) -&gt; {ok, #client{}} | {error, term()}
</code></pre>

<pre>stream_request_body(Body::term(), Client::#client{}) -&gt; {ok, #client{}} | {error, term()}</pre>
<br></br>


stream the request body. It isued after sending a request using
the `request` and `send_request` functions.
the `request` and `send_request` functions.
22 changes: 11 additions & 11 deletions doc/hackney_app.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@


#Module hackney_app#
# Module hackney_app #
* [Function Index](#index)
* [Function Details](#functions)

__Behaviours:__ [`application`](application.md).
<a name="index"></a>

__Behaviours:__ [`application`](application.md).<a name="index"></a>

##Function Index##
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#ensure_deps_started-0">ensure_deps_started/0</a></td><td></td></tr><tr><td valign="top"><a href="#start-2">start/2</a></td><td></td></tr><tr><td valign="top"><a href="#stop-1">stop/1</a></td><td></td></tr></table>


<a name="functions"></a>

##Function Details##
## Function Details ##

<a name="ensure_deps_started-0"></a>

###ensure_deps_started/0##

### ensure_deps_started/0 ###

`ensure_deps_started() -> any()`

<a name="start-2"></a>

###start/2##
<a name="start-2"></a>

### start/2 ###

`start(StartType, StartArgs) -> any()`

<a name="stop-1"></a>

###stop/1##
<a name="stop-1"></a>

### stop/1 ###

`stop(State) -> any()`


85 changes: 58 additions & 27 deletions doc/hackney_deps.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@


#Module hackney_deps#
# Module hackney_deps #
* [Function Index](#index)
* [Function Details](#functions)


<a name="index"></a>

##Function Index##
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#deps_on_path-0">deps_on_path/0</a></td><td>List of project dependencies on the path.</td></tr><tr><td valign="top"><a href="#ensure-0">ensure/0</a></td><td>Ensure that the ebin and include paths for dependencies of
Expand All @@ -18,85 +18,116 @@ code path.</td></tr></table>

<a name="functions"></a>

##Function Details##
## Function Details ##

<a name="deps_on_path-0"></a>

###deps_on_path/0##
### deps_on_path/0 ###


<pre>deps_on_path() -&gt; [ProjNameAndVers]</pre>
<pre><code>
deps_on_path() -&gt; [ProjNameAndVers]
</code></pre>

<br></br>


List of project dependencies on the path.<a name="ensure-0"></a>
List of project dependencies on the path.
<a name="ensure-0"></a>

### ensure/0 ###

###ensure/0##

<pre><code>
ensure() -&gt; ok
</code></pre>

<pre>ensure() -&gt; ok</pre>
<br></br>


Ensure that the ebin and include paths for dependencies of
this application are on the code path. Equivalent to
ensure(?Module).<a name="ensure-1"></a>
ensure(?Module).
<a name="ensure-1"></a>

###ensure/1##
### ensure/1 ###


<pre>ensure(Module) -&gt; ok</pre>
<pre><code>
ensure(Module) -&gt; ok
</code></pre>

<br></br>


Ensure that all ebin and include paths for dependencies
of the application for Module are on the code path.<a name="get_base_dir-0"></a>
of the application for Module are on the code path.
<a name="get_base_dir-0"></a>

### get_base_dir/0 ###

###get_base_dir/0##

<pre><code>
get_base_dir() -&gt; string()
</code></pre>

<pre>get_base_dir() -&gt; string()</pre>
<br></br>


Return the application directory for this application. Equivalent to
get_base_dir(?MODULE).<a name="get_base_dir-1"></a>
get_base_dir(?MODULE).
<a name="get_base_dir-1"></a>

###get_base_dir/1##
### get_base_dir/1 ###


<pre>get_base_dir(Module) -&gt; string()</pre>
<pre><code>
get_base_dir(Module) -&gt; string()
</code></pre>

<br></br>


Return the application directory for Module. It assumes Module is in
a standard OTP layout application in the ebin or src directory.<a name="local_path-1"></a>
a standard OTP layout application in the ebin or src directory.
<a name="local_path-1"></a>

### local_path/1 ###

###local_path/1##

<pre><code>
local_path(Components) -&gt; string()
</code></pre>

<pre>local_path(Components) -&gt; string()</pre>
<br></br>


Return an application-relative directory for this application.
Equivalent to local_path(Components, ?MODULE).<a name="local_path-2"></a>
Equivalent to local_path(Components, ?MODULE).
<a name="local_path-2"></a>

###local_path/2##
### local_path/2 ###


<pre>local_path(Components::[string()], Module) -&gt; string()</pre>
<pre><code>
local_path(Components::[string()], Module) -&gt; string()
</code></pre>

<br></br>


Return an application-relative directory from Module's application.<a name="new_siblings-1"></a>
Return an application-relative directory from Module's application.
<a name="new_siblings-1"></a>

### new_siblings/1 ###

###new_siblings/1##

<pre><code>
new_siblings(Module) -&gt; [Dir]
</code></pre>

<pre>new_siblings(Module) -&gt; [Dir]</pre>
<br></br>


Find new siblings paths relative to Module that aren't already on the
code path.
code path.
15 changes: 7 additions & 8 deletions doc/hackney_form.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@


#Module hackney_form#
# Module hackney_form #
* [Description](#description)
* [Function Index](#index)
* [Function Details](#functions)
Expand All @@ -10,28 +10,27 @@ module to encode/decode forms.

<a name="index"></a>

##Function Index##
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#decode_form-1">decode_form/1</a></td><td></td></tr><tr><td valign="top"><a href="#encode_form-1">encode_form/1</a></td><td>encode a list of properties in a form.</td></tr></table>


<a name="functions"></a>

##Function Details##
## Function Details ##

<a name="decode_form-1"></a>

###decode_form/1##

### decode_form/1 ###

`decode_form(Bin) -> any()`

<a name="encode_form-1"></a>

###encode_form/1##
<a name="encode_form-1"></a>

### encode_form/1 ###

`encode_form(KVs) -> any()`

encode a list of properties in a form.
encode a list of properties in a form.
42 changes: 21 additions & 21 deletions doc/hackney_headers.md
Original file line number Diff line number Diff line change
@@ -1,82 +1,82 @@


#Module hackney_headers#
# Module hackney_headers #
* [Function Index](#index)
* [Function Details](#functions)


<a name="index"></a>

##Function Index##
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#delete-2">delete/2</a></td><td></td></tr><tr><td valign="top"><a href="#fold-3">fold/3</a></td><td></td></tr><tr><td valign="top"><a href="#get_value-2">get_value/2</a></td><td></td></tr><tr><td valign="top"><a href="#get_value-3">get_value/3</a></td><td></td></tr><tr><td valign="top"><a href="#insert-3">insert/3</a></td><td></td></tr><tr><td valign="top"><a href="#new-0">new/0</a></td><td></td></tr><tr><td valign="top"><a href="#new-1">new/1</a></td><td></td></tr><tr><td valign="top"><a href="#to_list-1">to_list/1</a></td><td></td></tr><tr><td valign="top"><a href="#update-2">update/2</a></td><td></td></tr></table>


<a name="functions"></a>

##Function Details##
## Function Details ##

<a name="delete-2"></a>

###delete/2##

### delete/2 ###

`delete(Key, Headers) -> any()`

<a name="fold-3"></a>

###fold/3##
<a name="fold-3"></a>

### fold/3 ###

`fold(Fun, Acc0, Headers) -> any()`

<a name="get_value-2"></a>

###get_value/2##
<a name="get_value-2"></a>

### get_value/2 ###

`get_value(Key, Headers) -> any()`

<a name="get_value-3"></a>

###get_value/3##
<a name="get_value-3"></a>

### get_value/3 ###

`get_value(Key, Headers, Default) -> any()`

<a name="insert-3"></a>

###insert/3##
<a name="insert-3"></a>

### insert/3 ###

`insert(Key, Value, Headers) -> any()`

<a name="new-0"></a>

###new/0##
<a name="new-0"></a>

### new/0 ###

`new() -> any()`

<a name="new-1"></a>

###new/1##
<a name="new-1"></a>

### new/1 ###

`new(D) -> any()`

<a name="to_list-1"></a>

###to_list/1##
<a name="to_list-1"></a>

### to_list/1 ###

`to_list(Headers) -> any()`

<a name="update-2"></a>

###update/2##
<a name="update-2"></a>

### update/2 ###

`update(Headers, KVs) -> any()`


24 changes: 12 additions & 12 deletions doc/hackney_multipart.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@


#Module hackney_multipart#
# Module hackney_multipart #
* [Description](#description)
* [Function Index](#index)
* [Function Details](#functions)
Expand All @@ -10,41 +10,41 @@ module to encode/decode forms.

<a name="index"></a>

##Function Index##
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#boundary-0">boundary/0</a></td><td></td></tr><tr><td valign="top"><a href="#decode_form-1">decode_form/1</a></td><td></td></tr><tr><td valign="top"><a href="#encode_form-1">encode_form/1</a></td><td>encode a list of properties in a form.</td></tr><tr><td valign="top"><a href="#stream-2">stream/2</a></td><td></td></tr></table>


<a name="functions"></a>

##Function Details##
## Function Details ##

<a name="boundary-0"></a>

###boundary/0##

### boundary/0 ###

`boundary() -> any()`

<a name="decode_form-1"></a>

###decode_form/1##
<a name="decode_form-1"></a>

### decode_form/1 ###

`decode_form(X1) -> any()`

<a name="encode_form-1"></a>

###encode_form/1##
<a name="encode_form-1"></a>

### encode_form/1 ###

`encode_form(KVs) -> any()`

encode a list of properties in a form.<a name="stream-2"></a>

###stream/2##
encode a list of properties in a form.
<a name="stream-2"></a>

### stream/2 ###

`stream(X1, Client) -> any()`


105 changes: 52 additions & 53 deletions doc/hackney_pool.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@


#Module hackney_pool#
# Module hackney_pool #
* [Description](#description)
* [Function Index](#index)
* [Function Details](#functions)


pool of sockets connections.
__Behaviours:__ [`gen_server`](gen_server.md).
<a name="index"></a>

__Behaviours:__ [`gen_server`](gen_server.md).<a name="index"></a>

##Function Index##
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#child_spec-2">child_spec/2</a></td><td>return a child spec suitable for embeding your pool in the
Expand All @@ -19,141 +19,140 @@ supervisor.</td></tr><tr><td valign="top"><a href="#code_change-3">code_change/3

<a name="functions"></a>

##Function Details##
## Function Details ##

<a name="child_spec-2"></a>

###child_spec/2##

### child_spec/2 ###

`child_spec(Name, Options0) -> any()`

return a child spec suitable for embeding your pool in the
supervisor<a name="code_change-3"></a>

###code_change/3##
supervisor
<a name="code_change-3"></a>

### code_change/3 ###

`code_change(OldVsn, State, Extra) -> any()`

<a name="handle_call-3"></a>

###handle_call/3##
<a name="handle_call-3"></a>

### handle_call/3 ###

`handle_call(X1, From, State) -> any()`

<a name="handle_cast-2"></a>

###handle_cast/2##
<a name="handle_cast-2"></a>

### handle_cast/2 ###

`handle_cast(Msg, State) -> any()`

<a name="handle_info-2"></a>

###handle_info/2##
<a name="handle_info-2"></a>

### handle_info/2 ###

`handle_info(X1, State) -> any()`

<a name="init-1"></a>

###init/1##
<a name="init-1"></a>

### init/1 ###

`init(Options) -> any()`

<a name="max_poolsize-1"></a>

###max_poolsize/1##
<a name="max_poolsize-1"></a>

### max_poolsize/1 ###

`max_poolsize(PidOrName) -> any()`

get max pool size<a name="pool_size-1"></a>

###pool_size/1##
get max pool size
<a name="pool_size-1"></a>

### pool_size/1 ###

`pool_size(PidOrName) -> any()`

get total pool size<a name="pool_size-2"></a>

###pool_size/2##
get total pool size
<a name="pool_size-2"></a>

### pool_size/2 ###

`pool_size(PidOrName, X2) -> any()`

get the pool size for `{Transport, Host0, Port}`<a name="release-3"></a>

###release/3##
get the pool size for `{Transport, Host0, Port}`
<a name="release-3"></a>

### release/3 ###

`release(PidOrName, X2, Socket) -> any()`

release a socket in the pool<a name="set_poolsize-2"></a>

###set_poolsize/2##
release a socket in the pool
<a name="set_poolsize-2"></a>

### set_poolsize/2 ###

`set_poolsize(PidOrName, NewSize) -> any()`

change the pool size<a name="set_timeout-2"></a>

###set_timeout/2##
change the pool size
<a name="set_timeout-2"></a>

### set_timeout/2 ###

`set_timeout(PidOrName, NewTimeout) -> any()`

change the connection timeout
<a name="socket-2"></a>

###socket/2##
<a name="socket-2"></a>

### socket/2 ###

`socket(PidOrName, X2) -> any()`

fetch a socket from the pool<a name="start_link-0"></a>

###start_link/0##
fetch a socket from the pool
<a name="start_link-0"></a>

### start_link/0 ###

`start_link() -> any()`

<a name="start_link-1"></a>

###start_link/1##
<a name="start_link-1"></a>

### start_link/1 ###

`start_link(Options0) -> any()`

<a name="start_pool-2"></a>

###start_pool/2##
<a name="start_pool-2"></a>

### start_pool/2 ###

`start_pool(Name, Options) -> any()`

start a pool<a name="stop_pool-1"></a>

###stop_pool/1##
start a pool
<a name="stop_pool-1"></a>

### stop_pool/1 ###

`stop_pool(Name) -> any()`

stop a pool<a name="terminate-2"></a>

###terminate/2##
stop a pool
<a name="terminate-2"></a>

### terminate/2 ###

`terminate(Reason, State) -> any()`

<a name="timeout-1"></a>

###timeout/1##
<a name="timeout-1"></a>

### timeout/1 ###

`timeout(PidOrName) -> any()`

get timeout
get timeout
37 changes: 20 additions & 17 deletions doc/hackney_request.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,64 @@


#Module hackney_request#
# Module hackney_request #
* [Description](#description)
* [Function Index](#index)
* [Function Details](#functions)


module handling the request.

<a name="index"></a>

##Function Index##
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#end_stream_body-1">end_stream_body/1</a></td><td></td></tr><tr><td valign="top"><a href="#perform-2">perform/2</a></td><td></td></tr><tr><td valign="top"><a href="#send-2">send/2</a></td><td></td></tr><tr><td valign="top"><a href="#send_chunk-2">send_chunk/2</a></td><td></td></tr><tr><td valign="top"><a href="#sendfile-2">sendfile/2</a></td><td></td></tr><tr><td valign="top"><a href="#stream_body-2">stream_body/2</a></td><td></td></tr></table>
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#end_stream_body-1">end_stream_body/1</a></td><td></td></tr><tr><td valign="top"><a href="#perform-2">perform/2</a></td><td></td></tr><tr><td valign="top"><a href="#send-2">send/2</a></td><td></td></tr><tr><td valign="top"><a href="#send_chunk-2">send_chunk/2</a></td><td></td></tr><tr><td valign="top"><a href="#sendfile-3">sendfile/3</a></td><td></td></tr><tr><td valign="top"><a href="#stream_body-2">stream_body/2</a></td><td></td></tr></table>


<a name="functions"></a>

##Function Details##
## Function Details ##

<a name="end_stream_body-1"></a>

###end_stream_body/1##

### end_stream_body/1 ###

`end_stream_body(Client) -> any()`

<a name="perform-2"></a>

###perform/2##
<a name="perform-2"></a>

### perform/2 ###

`perform(Client0, X2) -> any()`

<a name="send-2"></a>

###send/2##
<a name="send-2"></a>

### send/2 ###

`send(Client, Data) -> any()`

<a name="send_chunk-2"></a>

###send_chunk/2##
<a name="send_chunk-2"></a>

### send_chunk/2 ###

`send_chunk(Client, Data) -> any()`

<a name="sendfile-2"></a>

###sendfile/2##
<a name="sendfile-3"></a>

### sendfile/3 ###

`sendfile(FileName, Opts, Client) -> any()`

`sendfile(FileName, Client) -> any()`

<a name="stream_body-2"></a>

###stream_body/2##
### stream_body/2 ###

`stream_body(Func, Client) -> any()`

`stream_body(Body, Client) -> any()`

71 changes: 45 additions & 26 deletions doc/hackney_response.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@


#Module hackney_response#
# Module hackney_response #
* [Description](#description)
* [Function Index](#index)
* [Function Details](#functions)


module handling the response.

<a name="index"></a>

##Function Index##
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#body-1">body/1</a></td><td>Return the full body sent with the request.</td></tr><tr><td valign="top"><a href="#body-2">body/2</a></td><td>Return the full body sent with the request as long as the body
Expand All @@ -16,82 +19,98 @@ length doesn't go over MaxLength.</td></tr><tr><td valign="top"><a href="#close-

<a name="functions"></a>

##Function Details##
## Function Details ##

<a name="body-1"></a>

###body/1##
### body/1 ###


<pre><code>
body(Client::#client{}) -&gt; {ok, binary(), #client{}} | {error, atom()}
</code></pre>

<pre>body(Client::#client{}) -&gt; {ok, binary(), #client{}} | {error, atom()}</pre>
<br></br>


Return the full body sent with the request.<a name="body-2"></a>
Return the full body sent with the request.
<a name="body-2"></a>

###body/2##
### body/2 ###


<pre>body(MaxLength::non_neg_integer() | infinity, Client::#client{}) -&gt; {ok, binary(), #client{}} | {error, atom()}</pre>
<br></br>
<pre><code>
body(MaxLength::non_neg_integer() | infinity, Client::#client{}) -&gt; {ok, binary(), #client{}} | {error, atom()}
</code></pre>

<br></br>



Return the full body sent with the request as long as the body
length doesn't go over MaxLength.



This is most useful to quickly be able to get the full body while
avoiding filling your memory with huge request bodies when you're
not expecting it.<a name="close-1"></a>
not expecting it.


###close/1##
When the response is larger than MaxLength, this function will return
the body it received up to the last chunk, which might be a bit more than MaxLength.
<a name="close-1"></a>

### close/1 ###

`close(Client) -> any()`


<a name="skip_body-1"></a>

###skip_body/1##
### skip_body/1 ###


<pre><code>
skip_body(Client::#client{}) -&gt; {ok, #client{}} | {error, atom()}
</code></pre>

<pre>skip_body(Client::#client{}) -&gt; {ok, #client{}} | {error, atom()}</pre>
<br></br>


<a name="start_response-1"></a>

###start_response/1##
<a name="start_response-1"></a>

### start_response/1 ###

`start_response(Client) -> any()`

Start the response It parse the request lines and headers.<a name="stream_body-1"></a>

###stream_body/1##
Start the response It parse the request lines and headers.
<a name="stream_body-1"></a>

### stream_body/1 ###

`stream_body(Client) -> any()`

<a name="stream_header-1"></a>

###stream_header/1##
<a name="stream_header-1"></a>

### stream_header/1 ###

`stream_header(Client) -> any()`

<a name="stream_headers-1"></a>

###stream_headers/1##
<a name="stream_headers-1"></a>

### stream_headers/1 ###

`stream_headers(Client) -> any()`

fetch all headers<a name="stream_status-1"></a>

###stream_status/1##
fetch all headers
<a name="stream_status-1"></a>

### stream_status/1 ###

`stream_status(Client) -> any()`

parse the status line
parse the status line
92 changes: 63 additions & 29 deletions doc/hackney_ssl_transport.md
Original file line number Diff line number Diff line change
@@ -1,111 +1,145 @@


#Module hackney_ssl_transport#
# Module hackney_ssl_transport #
* [Function Index](#index)
* [Function Details](#functions)


<a name="index"></a>

##Function Index##
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#close-1">close/1</a></td><td>Close a TCP socket.</td></tr><tr><td valign="top"><a href="#connect-3">connect/3</a></td><td></td></tr><tr><td valign="top"><a href="#controlling_process-2">controlling_process/2</a></td><td>Assign a new controlling process <em>Pid</em> to <em>Socket</em>.</td></tr><tr><td valign="top"><a href="#peername-1">peername/1</a></td><td>Return the address and port for the other end of a connection.</td></tr><tr><td valign="top"><a href="#recv-2">recv/2</a></td><td></td></tr><tr><td valign="top"><a href="#recv-3">recv/3</a></td><td>Receive a packet from a socket in passive mode.</td></tr><tr><td valign="top"><a href="#send-2">send/2</a></td><td>Send a packet on a socket.</td></tr><tr><td valign="top"><a href="#setopts-2">setopts/2</a></td><td>Set one or more options for a socket.</td></tr><tr><td valign="top"><a href="#sockname-1">sockname/1</a></td><td>Get the local address and port of a socket.</td></tr></table>
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#close-1">close/1</a></td><td>Close a TCP socket.</td></tr><tr><td valign="top"><a href="#connect-3">connect/3</a></td><td></td></tr><tr><td valign="top"><a href="#connect-4">connect/4</a></td><td></td></tr><tr><td valign="top"><a href="#controlling_process-2">controlling_process/2</a></td><td>Assign a new controlling process <em>Pid</em> to <em>Socket</em>.</td></tr><tr><td valign="top"><a href="#peername-1">peername/1</a></td><td>Return the address and port for the other end of a connection.</td></tr><tr><td valign="top"><a href="#recv-2">recv/2</a></td><td></td></tr><tr><td valign="top"><a href="#recv-3">recv/3</a></td><td>Receive a packet from a socket in passive mode.</td></tr><tr><td valign="top"><a href="#send-2">send/2</a></td><td>Send a packet on a socket.</td></tr><tr><td valign="top"><a href="#setopts-2">setopts/2</a></td><td>Set one or more options for a socket.</td></tr><tr><td valign="top"><a href="#sockname-1">sockname/1</a></td><td>Get the local address and port of a socket.</td></tr></table>


<a name="functions"></a>

##Function Details##
## Function Details ##

<a name="close-1"></a>

###close/1##
### close/1 ###


<pre>close(Socket::<a href="ssl.md#type-sslsocket">ssl:sslsocket()</a>) -> ok</pre>
<pre><code>
close(Socket::<a href="ssl.md#type-sslsocket">ssl:sslsocket()</a>) -&gt; ok
</code></pre>

<br></br>


Close a TCP socket.

__See also:__ [ssl:close/1](ssl.md#close-1).<a name="connect-3"></a>

###connect/3##
__See also:__ [ssl:close/1](ssl.md#close-1).
<a name="connect-3"></a>

### connect/3 ###

`connect(Host, Port, Opts) -> any()`


<a name="connect-4"></a>

### connect/4 ###

`connect(Host, Port, Opts, Timeout) -> any()`


<a name="controlling_process-2"></a>

###controlling_process/2##
### controlling_process/2 ###


<pre><code>
controlling_process(Socket::<a href="ssl.md#type-sslsocket">ssl:sslsocket()</a>, Pid::pid()) -&gt; ok | {error, closed | not_owner | atom()}
</code></pre>

<pre>controlling_process(Socket::<a href="ssl.md#type-sslsocket">ssl:sslsocket()</a>, Pid::pid()) -> ok | {error, closed | not_owner | atom()}</pre>
<br></br>


Assign a new controlling process _Pid_ to _Socket_.

__See also:__ [ssl:controlling_process/2](ssl.md#controlling_process-2).<a name="peername-1"></a>
__See also:__ [ssl:controlling_process/2](ssl.md#controlling_process-2).
<a name="peername-1"></a>

###peername/1##
### peername/1 ###


<pre>peername(Socket::<a href="ssl.md#type-sslsocket">ssl:sslsocket()</a>) -> {ok, {<a href="inet.md#type-ip_address">inet:ip_address()</a>, <a href="inet.md#type-port_number">inet:port_number()</a>}} | {error, atom()}</pre>
<pre><code>
peername(Socket::<a href="ssl.md#type-sslsocket">ssl:sslsocket()</a>) -&gt; {ok, {<a href="inet.md#type-ip_address">inet:ip_address()</a>, <a href="inet.md#type-port_number">inet:port_number()</a>}} | {error, atom()}
</code></pre>

<br></br>


Return the address and port for the other end of a connection.

__See also:__ [ssl:peername/1](ssl.md#peername-1).<a name="recv-2"></a>

###recv/2##
__See also:__ [ssl:peername/1](ssl.md#peername-1).
<a name="recv-2"></a>

### recv/2 ###

`recv(Socket, Length) -> any()`


<a name="recv-3"></a>

###recv/3##
### recv/3 ###


<pre><code>
recv(Socket::<a href="ssl.md#type-sslsocket">ssl:sslsocket()</a>, Length::non_neg_integer(), Timeout::timeout()) -&gt; {ok, any()} | {error, closed | atom()}
</code></pre>

<pre>recv(Socket::<a href="ssl.md#type-sslsocket">ssl:sslsocket()</a>, Length::non_neg_integer(), Timeout::timeout()) -> {ok, any()} | {error, closed | atom()}</pre>
<br></br>


Receive a packet from a socket in passive mode.

__See also:__ [ssl:recv/3](ssl.md#recv-3).<a name="send-2"></a>
__See also:__ [ssl:recv/3](ssl.md#recv-3).
<a name="send-2"></a>

###send/2##
### send/2 ###


<pre>send(Socket::<a href="ssl.md#type-sslsocket">ssl:sslsocket()</a>, Packet::iolist()) -> ok | {error, atom()}</pre>
<pre><code>
send(Socket::<a href="ssl.md#type-sslsocket">ssl:sslsocket()</a>, Packet::iolist()) -&gt; ok | {error, atom()}
</code></pre>

<br></br>


Send a packet on a socket.

__See also:__ [ssl:send/2](ssl.md#send-2).<a name="setopts-2"></a>
__See also:__ [ssl:send/2](ssl.md#send-2).
<a name="setopts-2"></a>

### setopts/2 ###

###setopts/2##

<pre><code>
setopts(Socket::<a href="ssl.md#type-sslsocket">ssl:sslsocket()</a>, Opts::list()) -&gt; ok | {error, atom()}
</code></pre>

<pre>setopts(Socket::<a href="ssl.md#type-sslsocket">ssl:sslsocket()</a>, Opts::list()) -> ok | {error, atom()}</pre>
<br></br>


Set one or more options for a socket.

__See also:__ [ssl:setopts/2](ssl.md#setopts-2).<a name="sockname-1"></a>
__See also:__ [ssl:setopts/2](ssl.md#setopts-2).
<a name="sockname-1"></a>

### sockname/1 ###

###sockname/1##

<pre><code>
sockname(Socket::<a href="ssl.md#type-sslsocket">ssl:sslsocket()</a>) -&gt; {ok, {<a href="inet.md#type-ip_address">inet:ip_address()</a>, <a href="inet.md#type-port_number">inet:port_number()</a>}} | {error, atom()}
</code></pre>

<pre>sockname(Socket::<a href="ssl.md#type-sslsocket">ssl:sslsocket()</a>) -> {ok, {<a href="inet.md#type-ip_address">inet:ip_address()</a>, <a href="inet.md#type-port_number">inet:port_number()</a>}} | {error, atom()}</pre>
<br></br>


Get the local address and port of a socket

__See also:__ [ssl:sockname/1](ssl.md#sockname-1).
__See also:__ [ssl:sockname/1](ssl.md#sockname-1).
18 changes: 9 additions & 9 deletions doc/hackney_sup.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@


#Module hackney_sup#
# Module hackney_sup #
* [Function Index](#index)
* [Function Details](#functions)

__Behaviours:__ [`supervisor`](supervisor.md).
<a name="index"></a>

__Behaviours:__ [`supervisor`](supervisor.md).<a name="index"></a>

##Function Index##
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#init-1">init/1</a></td><td></td></tr><tr><td valign="top"><a href="#start_link-0">start_link/0</a></td><td></td></tr></table>


<a name="functions"></a>

##Function Details##
## Function Details ##

<a name="init-1"></a>

###init/1##

### init/1 ###

`init(X1) -> any()`

<a name="start_link-0"></a>

###start_link/0##
<a name="start_link-0"></a>

### start_link/0 ###

`start_link() -> any()`


92 changes: 63 additions & 29 deletions doc/hackney_tcp_transport.md
Original file line number Diff line number Diff line change
@@ -1,111 +1,145 @@


#Module hackney_tcp_transport#
# Module hackney_tcp_transport #
* [Function Index](#index)
* [Function Details](#functions)


<a name="index"></a>

##Function Index##
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#close-1">close/1</a></td><td>Close a TCP socket.</td></tr><tr><td valign="top"><a href="#connect-3">connect/3</a></td><td></td></tr><tr><td valign="top"><a href="#controlling_process-2">controlling_process/2</a></td><td>Assign a new controlling process <em>Pid</em> to <em>Socket</em>.</td></tr><tr><td valign="top"><a href="#peername-1">peername/1</a></td><td>Return the address and port for the other end of a connection.</td></tr><tr><td valign="top"><a href="#recv-2">recv/2</a></td><td></td></tr><tr><td valign="top"><a href="#recv-3">recv/3</a></td><td>Receive a packet from a socket in passive mode.</td></tr><tr><td valign="top"><a href="#send-2">send/2</a></td><td>Send a packet on a socket.</td></tr><tr><td valign="top"><a href="#setopts-2">setopts/2</a></td><td>Set one or more options for a socket.</td></tr><tr><td valign="top"><a href="#sockname-1">sockname/1</a></td><td>Get the local address and port of a socket.</td></tr></table>
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#close-1">close/1</a></td><td>Close a TCP socket.</td></tr><tr><td valign="top"><a href="#connect-3">connect/3</a></td><td></td></tr><tr><td valign="top"><a href="#connect-4">connect/4</a></td><td></td></tr><tr><td valign="top"><a href="#controlling_process-2">controlling_process/2</a></td><td>Assign a new controlling process <em>Pid</em> to <em>Socket</em>.</td></tr><tr><td valign="top"><a href="#peername-1">peername/1</a></td><td>Return the address and port for the other end of a connection.</td></tr><tr><td valign="top"><a href="#recv-2">recv/2</a></td><td></td></tr><tr><td valign="top"><a href="#recv-3">recv/3</a></td><td>Receive a packet from a socket in passive mode.</td></tr><tr><td valign="top"><a href="#send-2">send/2</a></td><td>Send a packet on a socket.</td></tr><tr><td valign="top"><a href="#setopts-2">setopts/2</a></td><td>Set one or more options for a socket.</td></tr><tr><td valign="top"><a href="#sockname-1">sockname/1</a></td><td>Get the local address and port of a socket.</td></tr></table>


<a name="functions"></a>

##Function Details##
## Function Details ##

<a name="close-1"></a>

###close/1##
### close/1 ###


<pre>close(Socket::<a href="inet.md#type-socket">inet:socket()</a>) -> ok</pre>
<pre><code>
close(Socket::<a href="inet.md#type-socket">inet:socket()</a>) -&gt; ok
</code></pre>

<br></br>


Close a TCP socket.

__See also:__ [gen_tcp:close/1](gen_tcp.md#close-1).<a name="connect-3"></a>

###connect/3##
__See also:__ [gen_tcp:close/1](gen_tcp.md#close-1).
<a name="connect-3"></a>

### connect/3 ###

`connect(Host, Port, Opts) -> any()`


<a name="connect-4"></a>

### connect/4 ###

`connect(Host, Port, Opts, Timeout) -> any()`


<a name="controlling_process-2"></a>

###controlling_process/2##
### controlling_process/2 ###


<pre><code>
controlling_process(Socket::<a href="inet.md#type-socket">inet:socket()</a>, Pid::pid()) -&gt; ok | {error, closed | not_owner | atom()}
</code></pre>

<pre>controlling_process(Socket::<a href="inet.md#type-socket">inet:socket()</a>, Pid::pid()) -> ok | {error, closed | not_owner | atom()}</pre>
<br></br>


Assign a new controlling process _Pid_ to _Socket_.

__See also:__ [gen_tcp:controlling_process/2](gen_tcp.md#controlling_process-2).<a name="peername-1"></a>
__See also:__ [gen_tcp:controlling_process/2](gen_tcp.md#controlling_process-2).
<a name="peername-1"></a>

###peername/1##
### peername/1 ###


<pre>peername(Socket::<a href="inet.md#type-socket">inet:socket()</a>) -> {ok, {<a href="inet.md#type-ip_address">inet:ip_address()</a>, <a href="inet.md#type-port_number">inet:port_number()</a>}} | {error, atom()}</pre>
<pre><code>
peername(Socket::<a href="inet.md#type-socket">inet:socket()</a>) -&gt; {ok, {<a href="inet.md#type-ip_address">inet:ip_address()</a>, <a href="inet.md#type-port_number">inet:port_number()</a>}} | {error, atom()}
</code></pre>

<br></br>


Return the address and port for the other end of a connection.

__See also:__ [inet:peername/1](inet.md#peername-1).<a name="recv-2"></a>

###recv/2##
__See also:__ [inet:peername/1](inet.md#peername-1).
<a name="recv-2"></a>

### recv/2 ###

`recv(Socket, Length) -> any()`


<a name="recv-3"></a>

###recv/3##
### recv/3 ###


<pre><code>
recv(Socket::<a href="inet.md#type-socket">inet:socket()</a>, Length::non_neg_integer(), Timeout::timeout()) -&gt; {ok, any()} | {error, closed | atom()}
</code></pre>

<pre>recv(Socket::<a href="inet.md#type-socket">inet:socket()</a>, Length::non_neg_integer(), Timeout::timeout()) -> {ok, any()} | {error, closed | atom()}</pre>
<br></br>


Receive a packet from a socket in passive mode.

__See also:__ [gen_tcp:recv/3](gen_tcp.md#recv-3).<a name="send-2"></a>
__See also:__ [gen_tcp:recv/3](gen_tcp.md#recv-3).
<a name="send-2"></a>

###send/2##
### send/2 ###


<pre>send(Socket::<a href="inet.md#type-socket">inet:socket()</a>, Packet::iolist()) -> ok | {error, atom()}</pre>
<pre><code>
send(Socket::<a href="inet.md#type-socket">inet:socket()</a>, Packet::iolist()) -&gt; ok | {error, atom()}
</code></pre>

<br></br>


Send a packet on a socket.

__See also:__ [gen_tcp:send/2](gen_tcp.md#send-2).<a name="setopts-2"></a>
__See also:__ [gen_tcp:send/2](gen_tcp.md#send-2).
<a name="setopts-2"></a>

### setopts/2 ###

###setopts/2##

<pre><code>
setopts(Socket::<a href="inet.md#type-socket">inet:socket()</a>, Opts::list()) -&gt; ok | {error, atom()}
</code></pre>

<pre>setopts(Socket::<a href="inet.md#type-socket">inet:socket()</a>, Opts::list()) -> ok | {error, atom()}</pre>
<br></br>


Set one or more options for a socket.

__See also:__ [inet:setopts/2](inet.md#setopts-2).<a name="sockname-1"></a>
__See also:__ [inet:setopts/2](inet.md#setopts-2).
<a name="sockname-1"></a>

### sockname/1 ###

###sockname/1##

<pre><code>
sockname(Socket::<a href="inet.md#type-socket">inet:socket()</a>) -&gt; {ok, {<a href="inet.md#type-ip_address">inet:ip_address()</a>, <a href="inet.md#type-port_number">inet:port_number()</a>}} | {error, atom()}
</code></pre>

<pre>sockname(Socket::<a href="inet.md#type-socket">inet:socket()</a>) -> {ok, {<a href="inet.md#type-ip_address">inet:ip_address()</a>, <a href="inet.md#type-port_number">inet:port_number()</a>}} | {error, atom()}</pre>
<br></br>


Get the local address and port of a socket

__See also:__ [inet:sockname/1](inet.md#sockname-1).
__See also:__ [inet:sockname/1](inet.md#sockname-1).
4 changes: 2 additions & 2 deletions doc/hackney_transform.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@


#Module hackney_transform#
# Module hackney_transform #
* [Description](#description)


Expand All @@ -10,5 +10,5 @@ calls into hackney:request(Method, ...).

<a name="description"></a>

##Description##
## Description ##

63 changes: 41 additions & 22 deletions doc/hackney_url.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@


#Module hackney_url#
# Module hackney_url #
* [Description](#description)
* [Function Index](#index)
* [Function Details](#functions)
Expand All @@ -10,84 +10,103 @@ module to manage urls.

<a name="index"></a>

##Function Index##
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#parse_url-1">parse_url/1</a></td><td>Parse an url and return a #hackney_url record.</td></tr><tr><td valign="top"><a href="#transport_scheme-1">transport_scheme/1</a></td><td></td></tr><tr><td valign="top"><a href="#unparse_url-1">unparse_url/1</a></td><td></td></tr><tr><td valign="top"><a href="#urldecode-1">urldecode/1</a></td><td>Decode a URL encoded binary.</td></tr><tr><td valign="top"><a href="#urldecode-2">urldecode/2</a></td><td>Decode a URL encoded binary.</td></tr><tr><td valign="top"><a href="#urlencode-1">urlencode/1</a></td><td>URL encode a string binary.</td></tr><tr><td valign="top"><a href="#urlencode-2">urlencode/2</a></td><td>URL encode a string binary.</td></tr></table>


<a name="functions"></a>

##Function Details##
## Function Details ##

<a name="parse_url-1"></a>

###parse_url/1##
### parse_url/1 ###


<pre>parse_url(URL::binary() | list()) -> <a href="#type-hackney_url">hackney_url()</a></pre>
<br></br>
<pre><code>
parse_url(URL::binary() | list()) -&gt; <a href="#type-hackney_url">hackney_url()</a>
</code></pre>

<br></br>

Parse an url and return a #hackney_url record.<a name="transport_scheme-1"></a>

###transport_scheme/1##
Parse an url and return a #hackney_url record.
<a name="transport_scheme-1"></a>

### transport_scheme/1 ###

`transport_scheme(X1) -> any()`

<a name="unparse_url-1"></a>

###unparse_url/1##
<a name="unparse_url-1"></a>

### unparse_url/1 ###

`unparse_url(Hackney_url) -> any()`


<a name="urldecode-1"></a>

###urldecode/1##
### urldecode/1 ###


<pre><code>
urldecode(Bin::binary()) -&gt; binary()
</code></pre>

<pre>urldecode(Bin::binary()) -&gt; binary()</pre>
<br></br>


Equivalent to [`urldecode(Bin, crash)`](#urldecode-2).

Decode a URL encoded binary.<a name="urldecode-2"></a>
Decode a URL encoded binary.
<a name="urldecode-2"></a>

### urldecode/2 ###

###urldecode/2##

<pre><code>
urldecode(Bin::binary(), OnError::crash | skip) -&gt; binary()
</code></pre>

<pre>urldecode(Bin::binary(), OnError::crash | skip) -&gt; binary()</pre>
<br></br>


Decode a URL encoded binary.
The second argument specifies how to handle percent characters that are not
followed by two valid hex characters. Use `skip` to ignore such errors,
if `crash` is used the function will fail with the reason `badarg`.<a name="urlencode-1"></a>
if `crash` is used the function will fail with the reason `badarg`.
<a name="urlencode-1"></a>

### urlencode/1 ###

###urlencode/1##

<pre><code>
urlencode(Bin::binary()) -&gt; binary()
</code></pre>

<pre>urlencode(Bin::binary()) -&gt; binary()</pre>
<br></br>


Equivalent to [`urlencode(Bin, [])`](#urlencode-2).

URL encode a string binary.<a name="urlencode-2"></a>
URL encode a string binary.
<a name="urlencode-2"></a>

### urlencode/2 ###

###urlencode/2##

<pre><code>
urlencode(Bin::binary(), Opts::[noplus | upper]) -&gt; binary()
</code></pre>

<pre>urlencode(Bin::binary(), Opts::[noplus | upper]) -&gt; binary()</pre>
<br></br>


URL encode a string binary.
The `noplus` option disables the default behaviour of quoting space
characters, `\s`, as `+`. The `upper` option overrides the default behaviour
of writing hex numbers using lowecase letters to using uppercase letters
instead.
instead.
81 changes: 50 additions & 31 deletions doc/hackney_util.md
Original file line number Diff line number Diff line change
@@ -1,111 +1,130 @@


#Module hackney_util#
# Module hackney_util #
* [Function Index](#index)
* [Function Details](#functions)


<a name="index"></a>

##Function Index##
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#char_to_lower-1">char_to_lower/1</a></td><td>Convert [A-Z] characters to lowercase.</td></tr><tr><td valign="top"><a href="#char_to_upper-1">char_to_upper/1</a></td><td>Convert [a-z] characters to uppercase.</td></tr><tr><td valign="top"><a href="#content_type-1">content_type/1</a></td><td></td></tr><tr><td valign="top"><a href="#is_ipv6-1">is_ipv6/1</a></td><td></td></tr><tr><td valign="top"><a href="#join-2">join/2</a></td><td></td></tr><tr><td valign="top"><a href="#to_binary-1">to_binary/1</a></td><td></td></tr><tr><td valign="top"><a href="#to_hex-1">to_hex/1</a></td><td></td></tr><tr><td valign="top"><a href="#to_lower-1">to_lower/1</a></td><td>Convert a binary string to lowercase.</td></tr><tr><td valign="top"><a href="#to_upper-1">to_upper/1</a></td><td></td></tr><tr><td valign="top"><a href="#token-2">token/2</a></td><td>Parse a token.</td></tr><tr><td valign="top"><a href="#token_ci-2">token_ci/2</a></td><td>Parse a case-insensitive token.</td></tr></table>


<a name="functions"></a>

##Function Details##
## Function Details ##

<a name="char_to_lower-1"></a>

###char_to_lower/1##
### char_to_lower/1 ###


<pre>char_to_lower(Ch::char()) -&gt; char()</pre>
<pre><code>
char_to_lower(Ch::char()) -&gt; char()
</code></pre>

<br></br>


Convert [A-Z] characters to lowercase.<a name="char_to_upper-1"></a>
Convert [A-Z] characters to lowercase.
<a name="char_to_upper-1"></a>

###char_to_upper/1##
### char_to_upper/1 ###


<pre>char_to_upper(Ch::char()) -&gt; char()</pre>
<br></br>
<pre><code>
char_to_upper(Ch::char()) -&gt; char()
</code></pre>

<br></br>

Convert [a-z] characters to uppercase.<a name="content_type-1"></a>

###content_type/1##
Convert [a-z] characters to uppercase.
<a name="content_type-1"></a>

### content_type/1 ###

`content_type(Name) -> any()`

<a name="is_ipv6-1"></a>

###is_ipv6/1##
<a name="is_ipv6-1"></a>

### is_ipv6/1 ###

`is_ipv6(Host) -> any()`

<a name="join-2"></a>

###join/2##
<a name="join-2"></a>

### join/2 ###

`join(L, Separator) -> any()`

<a name="to_binary-1"></a>

###to_binary/1##
<a name="to_binary-1"></a>

### to_binary/1 ###

`to_binary(V) -> any()`

<a name="to_hex-1"></a>

###to_hex/1##
<a name="to_hex-1"></a>

### to_hex/1 ###

`to_hex(Bin) -> any()`


<a name="to_lower-1"></a>

###to_lower/1##
### to_lower/1 ###


<pre>to_lower(L::binary()) -&gt; binary()</pre>
<br></br>
<pre><code>
to_lower(L::binary()) -&gt; binary()
</code></pre>

<br></br>

Convert a binary string to lowercase.<a name="to_upper-1"></a>

###to_upper/1##
Convert a binary string to lowercase.
<a name="to_upper-1"></a>

### to_upper/1 ###

`to_upper(U) -> any()`


<a name="token-2"></a>

###token/2##
### token/2 ###


<pre>token(Data::binary(), Fun::function()) -&gt; any()</pre>
<pre><code>
token(Data::binary(), Fun::function()) -&gt; any()
</code></pre>

<br></br>


Parse a token.<a name="token_ci-2"></a>
Parse a token.
<a name="token_ci-2"></a>

###token_ci/2##
### token_ci/2 ###


<pre>token_ci(Data::binary(), Fun::function()) -&gt; any()</pre>
<br></br>
<pre><code>
token_ci(Data::binary(), Fun::function()) -&gt; any()
</code></pre>

<br></br>



Parse a case-insensitive token.

Changes all characters to lowercase.

Changes all characters to lowercase.
12 changes: 7 additions & 5 deletions doc/overview.edoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
%%==============================================================================


@copyright 2012 Benoît Chesneau.
@version 0.4
@title hackney - simple HTTP client in Erlang
@copyright 2012-2013 Benoît Chesneau.
@version 0.4.1
@title hackney - HTTP client library in Erlang

@doc

# hackney

**hackney** is a simple HTTP client.
**hackney** is an HTTP client library for Erlan.

Main features:

Expand All @@ -33,7 +33,9 @@ Main features:
- SSL support
- Keepalive handling
- basic authentication
- stream the response
- stream the response and the requests
- multipart support (stramed or not)
- chunked encoding support
- Can send files using the sendfile API
- Chunked encoding support
- Optionnal socket pool
Expand Down
3 changes: 2 additions & 1 deletion include/hackney.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
netloc = netloc,
options = [],
socket = nil,
timeout = infinity,
recv_timeout = infinity,
follow_redirect = false,
max_redirect = 5,
force_redirect = false,
Expand All @@ -60,6 +60,7 @@
clen = nil,
te = nil,
connection = nil,
method = nil,
ctype = nil}).

-record(hackney_url, {
Expand Down
4 changes: 2 additions & 2 deletions package.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Expm.Package.new(name: "hackney", description: "Simple HTTP client in Erlang",
version: "0.4.0", keywords: ["http","client","binary"],
version: "0.4.1", keywords: ["http","client","binary"],
dependencies: ["mimetypes"],
licenses: [[name: "Apache License, Version 2.0", file: "LICENSE"]],
contributors: [[name: "Adam Rutkowski",
Expand All @@ -22,5 +22,5 @@ Expm.Package.new(name: "hackney", description: "Simple HTTP client in Erlang",
]],
maintainers: [[name: "Benoit Chesneau",
email: "bchesneau@gmail.com"]],
repositories: [[github: "benoitc/hackney", tag: "0.4.0"]])
repositories: [[github: "benoitc/hackney", tag: "0.4.1"]])

2 changes: 1 addition & 1 deletion src/hackney.app.src
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{application, hackney,
[
{description, "simple HTTP client"},
{vsn, "0.4.0"},
{vsn, "0.4.1"},
{registered, [hackney_pool]},
{applications, [kernel, stdlib, crypto, public_key, ssl]},
{mod, { hackney_app, nil}},
Expand Down
29 changes: 23 additions & 6 deletions src/hackney.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
%%% This file is part of hackney released under the Apache 2 license.
%%% See the NOTICE for more information.
%%%
%%% Copyright (c) 2012 Benoît Chesneau <benoitc@e-engura.org>
%%% Copyright (c) 2012-2013 Benoît Chesneau <benoitc@e-engura.org>
%%%

-module(hackney).
Expand Down Expand Up @@ -72,15 +72,18 @@ connect(Transport, Host, Port, #client{socket=Skt, options=Opts}=Client)
case pool(Client) of
undefined when UseDefaultPool == true ->
Opts1 = [{pool, default} | Opts],
socket_from_pool(default, {Transport, Host, Port},
Pool = whereis(hackney_pool),
socket_from_pool(Pool, {Transport, Host, Port},
Client#client{options=Opts1});
undefined ->
do_connect(Transport, Host, Port, Client);
Pool ->
socket_from_pool(Pool, {Transport, Host, Port}, Client)
end;
connect(Transport, Host, Port, Options) when is_list(Options) ->
connect(Transport, Host, Port, #client{options=Options}).
Timeout = proplists:get_value(recv_timeout, Options, infinity),
connect(Transport, Host, Port, #client{recv_timeout=Timeout,
options=Options}).

%% @doc close the client
close(Client) ->
Expand Down Expand Up @@ -159,6 +162,10 @@ request(Method, URL, Headers, Body) ->
%% <li>{proxy, proxy_options()}: to connect via a proxy.</li>
%% <li>insecure: to perform "insecure" SSL connections and
%% transfers without checking the certificate</li>
%% <li>{connect_timeout, infinity | integer()}: timeout used when
%% estabilishing a connection, in milliseconds. Default is 8000</li>
%% <li>{recv_timeout, infinity | integer()}: timeout used when
%% receiving a connection. Default is infinity</li>
%% </ul>
%%
%% </li>
Expand Down Expand Up @@ -243,6 +250,7 @@ end_stream_request_body(Client) ->
%% <ul>
%% <li>`eof': end the multipart request</li>
%% <li>`{Id, {File, FileName}}': to stream a file</li>
%% %% <li>`{Id, {File, FileName, FileOptions}}': to stream a file</li>
%% <li>`{data, {start, Id, DileName, ContentType}}': to start to stream
%% arbitrary binary content</li>
%% <li>`{data, Bin}`: send a binary. Use it only after emitting a
Expand All @@ -254,6 +262,12 @@ end_stream_request_body(Client) ->
%% <li>`{Id, Value}': send an arbitrary value as a boundary. Filename and
%% Id are identique</li>
%% </ul>
%% File options can be:
%% <ul>
%% <li>`{offset, Offset}': start to send file from this offset</li>
%% <li>`{bytes, Bytes}': number of bytes to send</li>
%% <li>`{chunk_size, ChunkSize}': the size of the chunk to send</li>
%% </ul>
stream_multipart_request(Body, Client) ->
hackney_multipart:stream(Body, Client).

Expand Down Expand Up @@ -332,13 +346,15 @@ maybe_proxy(Transport, Host, Port, Options)
connect(Transport, Host, Port, Options)
end.

connect_proxy(ProxyUrl, Host, Port, ProxyOpts, Options) ->
connect_proxy(ProxyUrl, Host, Port, ProxyOpts0, Options) ->
Host = iolist_to_binary([Host, ":", integer_to_list(Port)]),
Headers = [{<<"Host">>, Host}],
Timeout = proplists:get_value(recv_timeout, Options, infinity),
ProxyOpts = [{recv_timeout, Timeout} | ProxyOpts0],
case request(connect, ProxyUrl, Headers, <<>>, ProxyOpts) of
{ok, 200, _, Client0} ->
Client = skip_body(Client0),
{ok, Client#client{options=Options}};
{ok, Client#client{recv_timeout=Timeout, options=Options}};
{ok, S, H, Client} ->
Body = body(Client),
{error, {proxy_connection, S, H, Body}};
Expand Down Expand Up @@ -366,6 +382,7 @@ socket_from_pool(Pool, {Transport, Host, Port}=Key,

do_connect(Transport, Host, Port, #client{options=Opts}=Client) ->
ConnectOpts0 = proplists:get_value(connect_options, Opts, []),
ConnectTimeout = proplists:get_value(connect_timeout, Opts, 8000),

%% handle ipv6
ConnectOpts1 = case hackney_util:is_ipv6(Host) of
Expand All @@ -390,7 +407,7 @@ do_connect(Transport, Host, Port, #client{options=Opts}=Client) ->
ConnectOpts1
end,

case Transport:connect(Host, Port, ConnectOpts) of
case Transport:connect(Host, Port, ConnectOpts, ConnectTimeout) of
{ok, Skt} ->
FollowRedirect = proplists:get_value(follow_redirect,
Opts, false),
Expand Down
3 changes: 2 additions & 1 deletion src/hackney_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
%%% This file is part of hackney released under the Apache 2 license.
%%% See the NOTICE for more information.
%%%
%%% Copyright (c) 2012 Benoît Chesneau <benoitc@e-engura.org>
%%% Copyright (c) 2012-2013 Benoît Chesneau <benoitc@e-engura.org>
%%%

-module(hackney_app).

Expand Down
3 changes: 2 additions & 1 deletion src/hackney_deps.erl
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
%%% -*- erlang -*-
%%%
%%% This file is part of couchbeam released under the MIT license.
%%% This file is part of hackney released under the Apache 2 license.
%%% See the NOTICE for more information.
%%%

-module(hackney_deps).
-author('Justin Sheehy <justin@basho.com>').
Expand Down
4 changes: 2 additions & 2 deletions src/hackney_form.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
%%% This file is part of hackney released under the Apache 2 license.
%%% See the NOTICE for more information.
%%%
%%% Copyright (c) 2012 Benoît Chesneau <benoitc@e-engura.org>
%%% Copyright (c) 2012-2013 Benoît Chesneau <benoitc@e-engura.org>
%%%

%% @doc module to encode/decode forms
Expand All @@ -22,7 +22,7 @@ encode_form(KVs) ->
encode_form([], Acc) ->
Lines = hackney_util:join(lists:reverse(Acc), <<"&">>),
CType = <<"application/x-www-form-urlencoded; charset=utf-8">>,
{erlang:length(Lines), CType, Lines};
{erlang:byte_size(Lines), CType, Lines};
encode_form([{K,V}|R], Acc) ->
K1 = hackney_url:urlencode(K),
V1 = hackney_url:urlencode(V),
Expand Down
2 changes: 1 addition & 1 deletion src/hackney_headers.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
%%% This file is part of hackney released under the Apache 2 license.
%%% See the NOTICE for more information.
%%%
%%% Copyright (c) 2012 Benoît Chesneau <benoitc@e-engura.org>
%%% Copyright (c) 2012-201 Benoît Chesneau <benoitc@e-engura.org>
%%%
-module(hackney_headers).

Expand Down
18 changes: 10 additions & 8 deletions src/hackney_multipart.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
%%% This file is part of hackney released under the Apache 2 license.
%%% See the NOTICE for more information.
%%%
%%% Copyright (c) 2012 Benoît Chesneau <benoitc@e-engura.org>
%%% Copyright (c) 2012-2013 Benoît Chesneau <benoitc@e-engura.org>
%%% Copyright (c) 2012 Ilya Khlopotov <ilya.khlopotov@gmail.com>
%%%

Expand Down Expand Up @@ -64,20 +64,22 @@ encode({Id, Value}, Boundary) ->


stream(eof, #client{mp_boundary=Boundary}=Client) ->
Line = <<"--", Boundary/binary, "--", "\r\n">>,
Line = <<"--", Boundary/binary, "--", "\r\n\r\n">>,
case hackney_request:stream_body(Line, Client) of
{ok, Client1} ->
hackney_request:end_stream_body(Client1);
Error ->
Error
end;
stream({Id, {file, Name}}, #client{mp_boundary=Boundary}=Client) ->
Field = atom_to_binary(Id, utf8),
stream({Id, {file, Name}}, Client) ->
stream({Id, {file, Name, []}}, Client);
stream({Id, {file, Name, _Opts}=File}, #client{mp_boundary=Boundary}=Client) ->
Field = field(Id),
CType = hackney_util:content_type(Name),
Bin = mp_header(Field, Name, CType, Boundary),
case hackney_request:stream_body(Bin, Client) of
{ok, Client1} ->
case hackney_request:sendfile(Name, Client1) of
case hackney_request:stream_body(File, Client1) of
{ok, Client2} ->
hackney_request:stream_body(<<"\r\n">>, Client2);
Error ->
Expand Down Expand Up @@ -107,11 +109,11 @@ stream({Id, Value}, #client{mp_boundary=Boundary}=Client) ->
%% internal functions

mp_header(Field, FileName, CType, Boundary) ->
FileName1 = hackney_util:to_binary(FileName),
Parts = [
<<"--", Boundary/binary>>,
<<"Content-Disposition: form-data; name=\"",
Field/binary, "\"; filename=\"", FileName/binary, "\"">>,
<<"Content-Type: ", CType/binary >>, <<>>],
<<"Content-Disposition: form-data; name=\"", Field/binary, "\"; filename=\"", FileName1/binary, "\"">>,
<<"Content-Type: ", CType/binary >>, <<>>, <<>>],
hackney_util:join(Parts, <<"\r\n">>).

field(V) when is_list(V) ->
Expand Down
6 changes: 3 additions & 3 deletions src/hackney_pool.erl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
%%% See the NOTICE for more information.
%%%
%%% Copyright (c) 2009, Erlang Training and Consulting Ltd.
%%% Copyright (c) 2012, Benoît Chesneau <benoitc@e-engura.org>
%%% Copyright (c) 2012-2013, Benoît Chesneau <benoitc@e-engura.org>

%% @doc pool of sockets connections
%%
Expand Down Expand Up @@ -120,15 +120,15 @@ handle_call({release, Key, Socket}, _From, State) ->
NewState = store_connection(Key, Socket, State),
{reply, ok, NewState};
handle_call(pool_size, _From, #state{sockets=Sockets}=State) ->
{ok, dict:size(Sockets), State};
{reply, dict:size(Sockets), State};
handle_call({pool_size, Key}, _From, #state{connections=Conns}=State) ->
Size = case dict:find(Key, Conns) of
{ok, Sockets} ->
length(Sockets);
error ->
0
end,
{ok, Size, State}.
{reply, Size, State}.

handle_cast({set_poolsize, NewSize}, State) ->
{noreply, State#state{pool_size=NewSize}};
Expand Down
145 changes: 123 additions & 22 deletions src/hackney_request.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,34 @@
%%%
%%% This file is part of hackney released under the Apache 2 license.
%%% See the NOTICE for more information.
%%%
%%% Copyright (c) 2012-2013 Benoît Chesneau <benoitc@e-engura.org>
%%%

%% @doc module handling the request

-module(hackney_request).

-include("hackney.hrl").

-export([perform/2,
send/2, send_chunk/2,
sendfile/2,
sendfile/3,
stream_body/2, end_stream_body/1]).

-define(CHUNK_SIZE, 20480).

perform(Client0, {Method0, Path, Headers0, Body0}) ->
Method = hackney_util:to_upper(hackney_util:to_binary(Method0)),

#client{host=Host, port=Port, options=Options} = Client0,

HostHdr = iolist_to_binary([Host, ":", integer_to_list(Port)]),
HostHdr = case is_default_port(Client0) of
true ->
list_to_binary(Host);
false ->
iolist_to_binary([Host, ":", integer_to_list(Port)])
end,

%% make header dict
DefaultHeaders0 = [{<<"Host">>, HostHdr},
Expand Down Expand Up @@ -81,14 +91,15 @@ perform(Client0, {Method0, Path, Headers0, Body0}) ->
%% send headers data
case hackney_request:send(Client, HeadersData) of
ok when Body =:= stream ->
{ok, Client#client{response_state=stream}};
{ok, Client#client{response_state=stream, method=Method}};
ok ->
case stream_body(Body, Client) of
{error, _Reason}=E ->
E;
{ok, Client2} ->
case end_stream_body(Client2) of
{ok, FinalClient} ->
{ok, Client3} ->
FinalClient = Client3#client{method=Method},
hackney_response:start_response(FinalClient);
Error ->
Error
Expand All @@ -102,6 +113,34 @@ stream_body(eof, Client) ->
{ok, Client#client{response_state=waiting}};
stream_body(<<>>, Client) ->
{ok, Client#client{response_state=waiting}};
stream_body(Func, Client) when is_function(Func) ->
case Func() of
{ok, Data} ->
case stream_body(Data, Client) of
{ok, Client1} ->
stream_body(Func, Client1);
Error ->
Error
end;
eof ->
stream_body(eof, Client);
Err ->
Err
end;
stream_body({Func, State}, Client) when is_function(Func) ->
case Func(State) of
{ok, Data, NewState} ->
case stream_body(Data, Client) of
{ok, Client1} ->
stream_body({Func, NewState}, Client1);
Error ->
Error
end;
eof ->
stream_body(eof, Client);
Err ->
Err
end;
stream_body(Body, #client{req_chunk_size=ChunkSize, send_fun=Send}=Client)
when is_binary(Body) ->

Expand Down Expand Up @@ -130,9 +169,11 @@ stream_body(Body, #client{send_fun=Send}=Client) when is_list(Body) ->
Error
end;
stream_body({file, FileName}, Client) ->
case sendfile(FileName, Client) of
stream_body({file, FileName, []}, Client);
stream_body({file, FileName, Opts}, Client) ->
case sendfile(FileName, Opts, Client) of
{ok, _BytesSent} ->
{ok, Client#client{response_state=waiting}};
{ok, Client};
Error ->
Error
end.
Expand All @@ -145,15 +186,22 @@ send_chunk(Client, Data) ->
send(Client, [io_lib:format("~.16b\r\n", [Length]), Data,
<<"\r\n">>]).

sendfile(FileName, Opts, #client{transport=hackney_tcp_tansport, socket=Skt,
req_type=normal}) ->
Offset = proplists:get_value(offset, Opts, 0),
Bytes = proplists:get_value(bytes, Opts, 0),

sendfile(FileName, #client{transport=hackney_tcp_tansport, socket=Skt}) ->
file:sendfile(FileName, Skt);
sendfile(FileName, Client) ->
SendFileOpts = case proplists:get_value(chunk_size, Opts, ?CHUNK_SIZE) of
undefined -> Opts;
ChunkSize -> [{chunk_size, ChunkSize}]
end,
file:sendfile(FileName, Skt, Offset, Bytes, SendFileOpts);
sendfile(FileName, Opts, Client) ->
case file:open(FileName, [read, raw, binary]) of
{error, Reason} ->
{error, Reason};
{ok, Fd} ->
Res = sendfile_fallback(Fd, Client),
Res = sendfile_fallback(Fd, Opts, Client),
file:close(Fd),
Res
end.
Expand All @@ -170,6 +218,19 @@ handle_body(Headers, ReqType0, Body0, Client) ->
S= filelib:file_size(FileName),
CT = hackney_util:content_type(FileName),
{S, CT, Body0};
Func when is_function(Func) ->
CT = hackney_headers:get_value(<<"content-type">>, Headers,
<<"application/octet-stream">>),
S = hackney_headers:get_value(<<"content-length">>,
Headers),
{S, CT, Body0};
{Func, _} when is_function(Func) ->
CT = hackney_headers:get_value(<<"content-type">>, Headers,
<<"application/octet-stream">>),
S = hackney_headers:get_value(<<"content-length">>,
Headers),
{S, CT, Body0};

_ when is_list(Body0) -> % iolist case
S = erlang:length(Body0),
CT = hackney_headers:get_value(<<"content-type">>, Headers,
Expand All @@ -190,12 +251,30 @@ handle_body(Headers, ReqType0, Body0, Client) ->
Headers1 = hackney_headers:delete(<<"transfer-encoding">>,
Headers),
{hackney_headers:update(Headers1, NewHeadersKV), normal};
{chunked, F} when is_function(F) ->
NewHeadersKV = [{<<"Content-Type">>, CType}],
Headers1 = hackney_headers:delete(<<"content-length">>,
Headers),
{hackney_headers:update(Headers1, NewHeadersKV), chunked};
{chunked, {F, _}} when is_function(F) ->
NewHeadersKV = [{<<"Content-Type">>, CType}],
Headers1 = hackney_headers:delete(<<"content-length">>,
Headers),
{hackney_headers:update(Headers1, NewHeadersKV), chunked};

{chunked, _} ->
NewHeadersKV = [{<<"Content-Type">>, CType}],
Headers1 = hackney_headers:delete(<<"content-length">>,
Headers),
{hackney_headers:update(Headers1, NewHeadersKV), chunked};

{_, _} when CLen =:= undefined ->
NewHeadersKV = [{<<"Content-Type">>, CType},
{<<"Transfer-Encoding">>, <<"chunked">>}],
Headers1 = hackney_headers:delete(<<"content-length">>,
Headers),
{hackney_headers:update(Headers1, NewHeadersKV), chunked};

{_, _} ->
NewHeadersKV = [{<<"Content-Type">>, CType},
{<<"Content-Length">>, CLen}],
Expand All @@ -213,7 +292,7 @@ handle_multipart_body(Headers, ReqType, CLen, Client) ->

handle_multipart_body(Headers, ReqType, CLen, Boundary, Client) ->
CType = << "multipart/form-data; boundary=", Boundary/binary >>,
{NewHeaders, ReqType} = case {CLen, ReqType} of
{NewHeaders, ReqType1} = case {CLen, ReqType} of
{chunked, normal} ->
NewHeadersKV = [{<<"Content-Type">>, CType},
{<<"Transfer-Encoding">>, <<"chunked">>}],
Expand All @@ -233,7 +312,8 @@ handle_multipart_body(Headers, ReqType, CLen, Boundary, Client) ->
{<<"Content-Length">>, CLen}],
{hackney_headers:update(Headers, NewHeadersKV), normal}
end,
{NewHeaders, ReqType, stream, Client#client{mp_boundary=Boundary}}.
{NewHeaders, ReqType1, stream, Client#client{response_state=stream,
mp_boundary=Boundary}}.

req_type(Headers) ->
TE = hackney_headers:get_value(<<"Transfer-Encoding">>, Headers, <<>>),
Expand All @@ -253,29 +333,42 @@ end_stream_body(Client) ->
{ok, Client#client{response_state=waiting}}.


sendfile_fallback(Fd, Client) ->
sendfile_fallback(Fd, Opts, Client) ->
Offset = proplists:get_value(offset, Opts, 0),
Bytes = proplists:get_value(bytes, Opts, 0),
ChunkSize = proplists:get_value(chunk_size, Opts, ?CHUNK_SIZE),

{ok, CurrPos} = file:position(Fd, {cur, 0}),
{ok, _NewPos} = file:position(Fd, {bof, 0}),
Res = sendfile_fallback(Fd, Client, 0),
{ok, _NewPos} = file:position(Fd, {bof, Offset}),
Res = sendfile_fallback(Fd, Bytes, ChunkSize, Client, 0),
file:position(Fd, {bof, CurrPos}),
Res.

sendfile_fallback(Fd, #client{req_chunk_size=ChunkSize}=Client, Old) ->
case file:read(Fd, ChunkSize) of

sendfile_fallback(Fd, Bytes, ChunkSize, #client{send_fun=Send}=Client, Sent)
when Bytes > Sent orelse Bytes =:= 0 ->

Length = if Bytes > 0 -> erlang:min(ChunkSize, Bytes - Sent);
true -> ChunkSize
end,

case file:read(Fd, Length) of
{ok, Data} ->
Len = iolist_size(Data),
case send(Client, Data) of
case Send(Client, Data) of
ok ->
sendfile_fallback(Fd, Client, Len+Old);
sendfile_fallback(Fd, Bytes, ChunkSize, Client,
Sent + Len);
Error ->
Error
end;
eof ->
{ok, Old};
{ok, Sent};
Error ->
Error
end.

end;
sendfile_fallback(_, _, _, _, Sent) ->
{ok, Sent}.

default_ua() ->
Version = case application:get_key(hackney, vsn) of
Expand All @@ -285,3 +378,11 @@ default_ua() ->
<< "0.0.0" >>
end,
<< "hackney/", Version/binary >>.


is_default_port(#client{transport=hackney_tcp_transport, port=80}) ->
true;
is_default_port(#client{transport=hackney_ssl_transport, port=443}) ->
true;
is_default_port(_) ->
false.
44 changes: 30 additions & 14 deletions src/hackney_response.erl
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
%%% See the NOTICE for more information.
%%%
%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%% Copyright (c) 2012 Benoît Chesneau <benoitc@e-engura.org>
%%% Copyright (c) 2012-2013 Benoît Chesneau <benoitc@e-engura.org>
%%%

%% @doc module handling the response

-module(hackney_response).

-include("hackney.hrl").
Expand Down Expand Up @@ -83,7 +86,7 @@ stream_headers(Client, Headers) ->
{header, KV, Client1} ->
stream_headers(Client1, [KV | Headers]);
{error, Reason, Acc} ->
{error, {Reason, Acc}, Headers, Client}
{error, {Reason, {Acc, Headers, Client}}}
end.


Expand Down Expand Up @@ -134,12 +137,12 @@ parse_header(Line, Client) ->
{header, {Key, Value}, Client1}.


stream_body(Client=#client{body_state=waiting, te=TE, clen=Length}) ->
stream_body(Client=#client{body_state=waiting, te=TE, clen=Length, method=Method}) ->
case TE of
<<"chunked">> ->
stream_body(Client#client{body_state=
{stream, fun te_chunked/2, {0, 0}, fun ce_identity/1}});
_ when Length =:= 0 ->
_ when Length =:= 0 orelse Method =:= <<"HEAD">> ->
{done, Client#client{body_state=done}};
_ ->
stream_body(Client#client{body_state=
Expand All @@ -156,13 +159,20 @@ stream_body(Client=#client{body_state=done}) ->

-spec stream_body_recv(#client{})
-> {ok, binary(), #client{}} | {error, atom()}.
stream_body_recv(Client=#client{
transport=Transport, socket=Socket, buffer=Buffer}) ->
case Transport:recv(Socket, 0, 5000) of
{ok, Data} -> transfer_decode(<< Buffer/binary, Data/binary >>,
Client);
{error, Reason} -> {error, Reason}
end.
stream_body_recv(Client=#client{buffer=Buffer, version=Version,
clen=CLen}) ->
case recv(Client) of
{ok, Data} -> transfer_decode(<< Buffer/binary, Data/binary >>,
Client);
{error, closed} when Version =:= {1, 0}, CLen =:= nil ->
{ok, Buffer, Client#client{socket=nil,
state = closed,
body_state=done,
buffer = <<>>}};
{error, closed} ->
{error, {closed, Buffer}};
{error, Reason} -> {error, Reason}
end.


%% @doc Return the full body sent with the request.
Expand All @@ -176,6 +186,9 @@ body(Client) ->
%% This is most useful to quickly be able to get the full body while
%% avoiding filling your memory with huge request bodies when you're
%% not expecting it.
%%
%% When the response is larger than MaxLength, this function will return
%% the body it received up to the last chunk, which might be a bit more than MaxLength.
-spec body(non_neg_integer() | infinity, #client{})
-> {ok, binary(), #client{}} | {error, atom()}.
body(MaxLength, Client) ->
Expand Down Expand Up @@ -272,7 +285,10 @@ read_body(MaxLength, Client, Acc) when MaxLength > byte_size(Acc) ->
{ok, Acc, Client2};
{error, Reason} ->
{error, Reason}
end.
end;

read_body(_MaxLength, Client, Acc) ->
{ok, Acc, Client}.


maybe_close(#client{version={Min,Maj}, connection=Connection}) ->
Expand Down Expand Up @@ -323,8 +339,8 @@ te_identity(Data, {Streamed, Total}) ->
ce_identity(Data) ->
{ok, Data}.

recv(#client{transport=Transport, socket=Skt}) ->
Transport:recv(Skt, 0).
recv(#client{transport=Transport, socket=Skt, recv_timeout=Timeout}) ->
Transport:recv(Skt, 0, Timeout).


close(#client{socket=nil}=Client) ->
Expand Down
10 changes: 7 additions & 3 deletions src/hackney_ssl_transport.erl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
%%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>

-module(hackney_ssl_transport).
-export([connect/3,
-export([connect/3, connect/4,
recv/3, recv/2,
send/2,
setopts/2,
Expand All @@ -15,9 +15,13 @@
close/1,
sockname/1]).

connect(Host, Port, Opts) when is_list(Host), is_integer(Port) ->
connect(Host, Port, Opts) ->
connect(Host, Port, Opts, infinity).

connect(Host, Port, Opts, Timeout) when is_list(Host), is_integer(Port),
(Timeout =:= infinity orelse is_integer(Timeout)) ->
ssl:connect(Host, Port,
Opts ++ [binary, {active, false}, {packet, raw}]).
Opts ++ [binary, {active, false}, {packet, raw}], Timeout).

recv(Socket, Length) ->
recv(Socket, Length, infinity).
Expand Down
10 changes: 7 additions & 3 deletions src/hackney_tcp_transport.erl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
%%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%%
-module(hackney_tcp_transport).
-export([connect/3,
-export([connect/3, connect/4,
recv/2, recv/3,
send/2,
setopts/2,
Expand All @@ -15,9 +15,13 @@
close/1,
sockname/1]).

connect(Host, Port, Opts) when is_list(Host), is_integer(Port) ->
connect(Host, Port, Opts) ->
connect(Host, Port, Opts, infinity).

connect(Host, Port, Opts, Timeout) when is_list(Host), is_integer(Port),
(Timeout =:= infinity orelse is_integer(Timeout)) ->
gen_tcp:connect(Host, Port,
Opts ++ [binary, {active, false}, {packet, raw}]).
Opts ++ [binary, {active, false}, {packet, raw}], Timeout).

recv(Socket, Length) ->
recv(Socket, Length, infinity).
Expand Down
4 changes: 2 additions & 2 deletions src/hackney_transform.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
%%% This file is part of hackney released under the Apache 2 license.
%%% See the NOTICE for more information.
%%%
%%% Copyright (c) 2012 Benoît Chesneau <benoitc@e-engura.org>
%%% Copyright (c) 2012-2013 Benoît Chesneau <benoitc@e-engura.org>

%% @doc The parse transform used for hackney request
%% This parse tansform rewrite functions calls to hackney:Method
%% calls into hackney:request(Method, ...).
%%
-module(hackney_transform).

-include("hackney.hrl").
-include("../include/hackney.hrl").

-export([parse_transform/2]).

Expand Down
2 changes: 1 addition & 1 deletion src/hackney_url.erl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
%%% See the NOTICE for more information.
%%%
%%% Copyright (c) 2011-2012, Loïc Hoguin <essen@ninenines.eu>
%%% Copyright (c) 2012 Benoît Chesneau <benoitc@e-engura.org>
%%% Copyright (c) 2012-2013 Benoît Chesneau <benoitc@e-engura.org>
%%%

%% @doc module to manage urls.
Expand Down
2 changes: 1 addition & 1 deletion src/hackney_util.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
%%% This file is part of hackney released under the Apache 2 license.
%%% See the NOTICE for more information.
%%%
%%% Copyright (c) 2012 Benoît Chesneau <benoitc@e-engura.org>
%%% Copyright (c) 2012-2013 Benoît Chesneau <benoitc@e-engura.org>
%%%
-module(hackney_util).

Expand Down