diff --git a/LICENCE b/LICENCE index 71fd1882..1f254344 100644 --- a/LICENCE +++ b/LICENCE @@ -1,4 +1,4 @@ -Copyright (c) 2009, Erlang Training and Consulting Ltd. +Copyright (c) 2009-2013, Erlang Solutions Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8,14 +8,14 @@ modification, are permitted provided that the following conditions are met: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Erlang Training and Consulting Ltd. nor the + * Neither the name of Erlang Solutions Ltd. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS'' +THIS SOFTWARE IS PROVIDED BY Erlang Solutions Ltd. ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE +ARE DISCLAIMED. IN NO EVENT SHALL Erlang Solutions Ltd. BE LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR diff --git a/README b/README deleted file mode 100644 index 7ca418e4..00000000 --- a/README +++ /dev/null @@ -1,12 +0,0 @@ -Dependencies: - * Erlang/OTP R13-B or newer - * Application compiler to build, kernel, stdlib and ssl to run - -Building: -For versions > 1.2.5, lhttpc is built using rebar. Take a look at http://bitbucket.org/basho/rebar/wiki/Home for more information. There is still a Makefile with some of the old make targets, such as all, doc, test etc. for those who prefer that. The makefile will however just call rebar. - -Configuration: (environment variables) - * connection_timeout: The time (in milliseconds) the client will try to - kepp a HTTP/1.1 connection open. Changing this value - in runtime has no effect, this can however be done - through lhttpc_manager:update_connection_timeout/1. diff --git a/README.md b/README.md new file mode 100644 index 00000000..cdd42ace --- /dev/null +++ b/README.md @@ -0,0 +1,101 @@ +# LHTTPC - Lightweight HTTP Client # + +Copyright (c) 2009-2013 Erlang Solutions Ltd. + +## Features + +Some of the basic features provided by Lhttpc: + +- HTTP basic auth +- SSL support +- Keepalive connections +- Pools for managing connections +- Support for IPv6 +- Optional automatic cookie handling +- Chunked encoding + +## Starting + +Download the sources or clone the git repository. Then you can build with: + +``` +make all +``` + +which will generate .beam files and documentation. To start the lhttpc OTP application, you first need to start the applications it depends on: + +``` +$ erl -pa ebin +1> application:start(crypto), +1> application:start(public_key), +1> application:start(ssl), +1> lhttpc:start(). +ok +``` + +## Usage +Lhttpc allows the user to send requests by always spawning a process for each of them, or reusing a single client process for several requests. It also allows the usage of pools of connections in order to keep connections alive and reuse them. + +### Send a simple request +A single request without using a client process, will just spawn a process, do the request, and then stop the process. + +```erlang +Method = get, +URL = "http://www.erlang-solutions.com", +Headers = [], +Timeout = 100, +{ok,{{StatusCode, Status}, Headers, Body}} = lhttpc:request(URL, Method, Headers, Timeout). +``` +Using the function `request/9` it is also possible to specify the target server using `Host`, `Port` and `Ssl` and a relative `Path`. All the available options are listed in the documentation. + +### Reuse a client process + +It is possible to first connect a client process to the target server, and then do a requests specifing just the relative Path: + +```erlang +{ok, Client} = lhttpc:connect_client("http://erlang-solutions.com", []), +{ok,{{StatusCode, Status}, Headers, Body}} = lhttpc:request_client(Client, "/", get, [], 100). +``` + +And then reuse the same client to do more requests to the same server. + +### Use connection pools + +Lhttpc supports pools of connections. They keep the connections to the different servers independently of the client processes. Therefore, if we do a requests specifing a pool, the client process will try to retrieve the connection from the pool if there is one, use it, and then return it to the pool before stopping. This makes it possible to share connections between different client processes and keep connections alive. + +```erlang +lhttpc:add_pool(my_pool), +lhttpc:request("http://www.erlang-solutions.com", get, [], [], 100, [{pool_options, [{pool, my_pool}]}]). +``` + +The `lhttpc_manager` module provides functions to retrive information about the pools: + +``` +>lhttpc_manager:connection_count(my_pool). +0 +>lhttpc:request("http://www.erlang-solutions.com", get, [], [], 100, [{pool_options, [{pool, my_pool}]}]). +>lhttpc_manager:connection_count(my_pool). +1 +> +>lhttpc_manager:list_pools(). +[{my_pool,[{max_pool_size,50},{timeout,300000}]}] +>lhttpc_manager:update_connection_timeout(my_pool, 1000). +ok +>lhttpc_manager:list_pools(). +[{my_pool,[{max_pool_size,50},{timeout,1000}]}] +``` + +### Automatic cookie handling +Lhttpc supports basic cookie handling. If you want the client process to automatically handle the cookies, use the option `{use_cookies, true}`. + +### Transfering the body by chunks + +If you want to send the body of the request by chunks, you can specify the `{partial_upload, true}` option. Then use the `send_body_part/2` and `send_body_part/3` functions to send the body parts. `http_eob` signals the end of the body. As an example: + +```erlang +{ok, Client} = lhttpc:connect_client("http://erlang-solutions.com", []), +lhttpc:request_client(Client, "/", get, [], [], 100, [{partial_upload, true}]), +lhttpc:send_body_part(Client, <<"some part of the body">>), +lhttpc:send_body_part(Client, <<"more body">>), +lhttpc:send_body_part(Client, http_eob). +``` \ No newline at end of file diff --git a/doc/overview.edoc b/doc/overview.edoc index d625b27d..2f78dec5 100644 --- a/doc/overview.edoc +++ b/doc/overview.edoc @@ -1,11 +1,28 @@ @author Oscar Hellström -@doc A lightweight HTTP client. -The only functions of much interest right now are {@link +@author Diana Parra Corbacho +@author Ramon Lastres Guerrero +@doc A Lightweight HTTP client. + +Users can send simple requests using standalone request functions {@link lhttpc:request/4}, {@link lhttpc:request/5}, {@link lhttpc:request/6} and {@link lhttpc:request/9}. -

Configuration

+It is also possible to independently create a client process that can be reused for several requests, using {@link connect_client/2} +and then generate requests with {@link lhttpc:request_client/5}, {@link lhttpc:request_client/6}, {@link lhttpc:request_client/7} and +{@link lhttpc:request_client/9}. + +It supports pools of connections that can be added and deleted: +{@link add_pool/1} +{@link add_pool/2} +{@link delete_pool/1} + +The {@link lhttpc_manager} module provides basic functionalities to handle the different pools. + +

Configuration Parameters

+Configuration parameters specified in the app.src file.

`connection_timeout'

The maximum time (in milliseconds) the client will keep a TCP connection -open to a server. +open to a server. Default parameter that can be overriden by the request options. +

`pool_size'

+The size of every pool created. Default parameter that can be overriden by the request options. @end diff --git a/include/lhttpc.hrl b/include/lhttpc.hrl index 331b9ad1..7375d0fd 100644 --- a/include/lhttpc.hrl +++ b/include/lhttpc.hrl @@ -1,5 +1,5 @@ %%% ---------------------------------------------------------------------------- -%%% Copyright (c) 2009, Erlang Training and Consulting Ltd. +%%% Copyright (c) 2009-2013, Erlang Solutions Ltd. %%% All rights reserved. %%% %%% Redistribution and use in source and binary forms, with or without @@ -32,3 +32,13 @@ user = "" :: string(), password = "" :: string() }). + +-record(lhttpc_cookie, { + name = "" :: string(), + value = "" :: string(), + expires = 'never' :: {{integer(), integer(), integer()}, + {integer(), integer(), integer()}} | atom(), + path = 'undefined' :: string() | atom(), + max_age = 'undefined' :: integer() | atom(), + timestamp = 'undefined' :: atom() | {integer(), integer(), integer()} +}). diff --git a/include/lhttpc_types.hrl b/include/lhttpc_types.hrl index 64bc9323..d8a2dd2e 100644 --- a/include/lhttpc_types.hrl +++ b/include/lhttpc_types.hrl @@ -1,5 +1,5 @@ %%% ---------------------------------------------------------------------------- -%%% Copyright (c) 2009, Erlang Training and Consulting Ltd. +%%% Copyright (c) 2009-2013, Erlang Solutions Ltd. %%% All rights reserved. %%% %%% Redistribution and use in source and binary forms, with or without @@ -9,14 +9,14 @@ %%% * Redistributions in binary form must reproduce the above copyright %%% notice, this list of conditions and the following disclaimer in the %%% documentation and/or other materials provided with the distribution. -%%% * Neither the name of Erlang Training and Consulting Ltd. nor the +%%% * Neither the name of Erlang Solutions Ltd. nor the %%% names of its contributors may be used to endorse or promote products %%% derived from this software without specific prior written permission. %%% -%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS'' +%%% THIS SOFTWARE IS PROVIDED BY Erlang Solutions Ltd. ''AS IS'' %%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE %%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE +%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Solutions Ltd. BE %%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR %%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, %%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR @@ -52,29 +52,34 @@ -type poolsize() :: non_neg_integer() | atom(). --type invalid_option() :: any(). - -type pool_id() :: pid() | atom(). --type destination() :: {string(), pos_integer(), boolean()}. +-type destination() :: {host(), pos_integer(), boolean()} | string(). -type raw_headers() :: [{atom() | binary() | string(), binary() | string()}]. -type partial_download_option() :: {'window_size', window_size()} | - {'part_size', non_neg_integer() | 'infinity'} | - invalid_option(). + {'part_size', non_neg_integer() | 'infinity'}. -type option() :: {'connect_timeout', timeout()} | {'send_retry', non_neg_integer()} | - {'partial_upload', non_neg_integer() | 'infinity'} | + {'partial_upload', boolean()} | {'partial_download', [partial_download_option()]} | {'connect_options', socket_options()} | + {'use_cookies', boolean()} | {'proxy', string()} | {'proxy_ssl_options', socket_options()} | - {'pool', pid() | atom()} | - invalid_option(). + {'pool_options', pool_options()}. + +-type pool_option() :: + {'pool_ensure', boolean()} | + {'pool_connection_timeout', pos_timeout()} | + {'pool_max_size' | integer()} | + {'pool', pool_id()}. + +-type pool_options() :: [pool_option()]. -type options() :: [option()]. @@ -86,13 +91,13 @@ -type window_size() :: non_neg_integer() | 'infinity'. --type upload_state() :: {pid(), window_size()}. +-type response() :: {{pos_integer(), string()}, headers(), body()}. --type body() :: binary() | - 'undefined' | % HEAD request. - pid(). % When partial_download option is used. +-type body() :: binary() | + 'undefined' | % HEAD request. + pid(). % When partial_download option is used. -type result() :: - {ok, {{pos_integer(), string()}, headers(), body()}} | - {ok, upload_state()} | + {ok, response()} | + {ok, partial_upload} | {error, atom()}. diff --git a/priv/edoc.css b/priv/edoc.css new file mode 100644 index 00000000..1d50defe --- /dev/null +++ b/priv/edoc.css @@ -0,0 +1,130 @@ +/* Baseline rhythm */ +body { + font-size: 16px; + font-family: Helvetica, sans-serif; + margin: 8px; +} + +p { + font-size: 1em; /* 16px */ + line-height: 1.5em; /* 24px */ + margin: 0 0 1.5em 0; +} + +h1 { + font-size: 1.5em; /* 24px */ + line-height: 1em; /* 24px */ + margin-top: 1em; + margin-bottom: 0em; +} + +h2 { + font-size: 1.375em; /* 22px */ + line-height: 1.0909em; /* 24px */ + margin-top: 1.0909em; + margin-bottom: 0em; +} + +h3 { + font-size: 1.25em; /* 20px */ + line-height: 1.2em; /* 24px */ + margin-top: 1.2em; + margin-bottom: 0em; +} + +h4 { + font-size: 1.125em; /* 18px */ + line-height: 1.3333em; /* 24px */ + margin-top: 1.3333em; + margin-bottom: 0em; +} + +.class-for-16px { + font-size: 1em; /* 16px */ + line-height: 1.5em; /* 24px */ + margin-top: 1.5em; + margin-bottom: 0em; +} + +.class-for-14px { + font-size: 0.875em; /* 14px */ + line-height: 1.7143em; /* 24px */ + margin-top: 1.7143em; + margin-bottom: 0em; +} + +ul { + margin: 0 0 1.5em 0; +} + +/* Customizations */ +body { + color: #333; +} + +tt, code, pre { + font-family: "Andale Mono", "Inconsolata", "Monaco", "DejaVu Sans Mono", monospaced; +} + +tt, code { font-size: 0.875em } + +pre { + font-size: 0.875em; /* 14px */ + line-height: 1.7143em; /* 24px */ + margin: 0 1em 1.7143em; + padding: 0 1em; + background: #eee; +} + +.navbar img, hr { display: none } + +table { + border-collapse: collapse; +} + +h1 { + border-left: 0.5em solid #fa0; + padding-left: 0.5em; +} + +h2.indextitle { + font-size: 1.25em; /* 20px */ + line-height: 1.2em; /* 24px */ + margin: -8px -8px 0.6em; + background-color: #fa0; + color: white; + padding: 0.3em; +} + +ul.index { + list-style: none; + margin-left: 0em; + padding-left: 0; +} + +ul.index li { + display: inline; + padding-right: 0.75em +} + +div.spec p { + margin-bottom: 0; + padding-left: 1.25em; + background-color: #eee; +} + +h3.function { + border-left: 0.5em solid #fa0; + padding-left: 0.5em; + background: #fc9; +} +a, a:visited, a:hover, a:active { color: #C60 } +h2 a, h3 a { color: #333 } + +i { + font-size: 0.875em; /* 14px */ + line-height: 1.7143em; /* 24px */ + margin-top: 1.7143em; + margin-bottom: 0em; + font-style: normal; +} diff --git a/rebar.config b/rebar.config index 8b424ed3..3f850748 100644 --- a/rebar.config +++ b/rebar.config @@ -3,3 +3,5 @@ {eunit_opts, [verbose, {report,{eunit_surefire,[{dir,"."}]}}]}. {cover_enabled, true}. {cover_export_enabled, true}. +{edoc_opts, [{preprocess, true}, + {stylesheet_file, "./priv/edoc.css"}]}. diff --git a/src/lhttpc.app.src b/src/lhttpc.app.src index e853322a..e100f9d7 100644 --- a/src/lhttpc.app.src +++ b/src/lhttpc.app.src @@ -1,7 +1,7 @@ %%% ---------------------------------------------------------------------------- -%%% Copyright (c) 2009, Erlang Training and Consulting Ltd. +%%% Copyright (c) 2009-2013, Erlang Solutions Ltd. %%% All rights reserved. -%%% +%%% %%% Redistribution and use in source and binary forms, with or without %%% modification, are permitted provided that the following conditions are met: %%% * Redistributions of source code must retain the above copyright @@ -9,14 +9,14 @@ %%% * Redistributions in binary form must reproduce the above copyright %%% notice, this list of conditions and the following disclaimer in the %%% documentation and/or other materials provided with the distribution. -%%% * Neither the name of Erlang Training and Consulting Ltd. nor the +%%% * Neither the name of Erlang Solutions Ltd. nor the %%% names of its contributors may be used to endorse or promote products %%% derived from this software without specific prior written permission. -%%% -%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS'' +%%% +%%% THIS SOFTWARE IS PROVIDED BY Erlang Solutions Ltd. ''AS IS'' %%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE %%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE +%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Solutions Ltd. BE %%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR %%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, %%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR @@ -25,13 +25,15 @@ %%% ---------------------------------------------------------------------------- %%% @author Oscar Hellström +%%% @author Diana Parra Corbacho +%%% @author Ramon Lastres Guerrero %%% @doc This is the specification for the lhttpc application. %%% @end {application, lhttpc, [{description, "Lightweight HTTP Client"}, {vsn, "1.2.6"}, {modules, []}, - {registered, [lhttpc_manager]}, + {registered, [lhttpc_sup, lhttpc_manager]}, {applications, [kernel, stdlib, ssl, crypto]}, {mod, {lhttpc, nil}}, {env, [{connection_timeout, 300000}, {pool_size, 50}]} diff --git a/src/lhttpc.erl b/src/lhttpc.erl index 8b3dc308..f4f38616 100644 --- a/src/lhttpc.erl +++ b/src/lhttpc.erl @@ -1,5 +1,5 @@ %%% ---------------------------------------------------------------------------- -%%% Copyright (c) 2009, Erlang Training and Consulting Ltd. +%%% Copyright (c) 2009-2013, Erlang Solutions Ltd. %%% All rights reserved. %%% %%% Redistribution and use in source and binary forms, with or without @@ -9,14 +9,14 @@ %%% * Redistributions in binary form must reproduce the above copyright %%% notice, this list of conditions and the following disclaimer in the %%% documentation and/or other materials provided with the distribution. -%%% * Neither the name of Erlang Training and Consulting Ltd. nor the +%%% * Neither the name of Erlang Solutions Ltd. nor the %%% names of its contributors may be used to endorse or promote products %%% derived from this software without specific prior written permission. %%% -%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS'' +%%% THIS SOFTWARE IS PROVIDED BY Erlang Solutions Ltd. ''AS IS'' %%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE %%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE +%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Solutions Ltd. BE %%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR %%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, %%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR @@ -26,7 +26,9 @@ %%------------------------------------------------------------------------------ %%% @author Oscar Hellström -%%% @doc Main interface to the lightweight http client. +%%% @author Diana Parra Corbacho +%%% @author Ramon Lastres Guerrero +%%% @doc Main interface to the lightweight http client %%% See {@link request/4}, {@link request/5} and {@link request/6} functions. %%% @end %%------------------------------------------------------------------------------ @@ -35,15 +37,18 @@ -export([start/0, stop/0, start/2, stop/1, request/4, request/5, request/6, request/9, + request_client/5, request_client/6, request_client/7, add_pool/1, add_pool/2, add_pool/3, delete_pool/1, + connect_client/2, + disconnect_client/1, send_body_part/2, send_body_part/3, send_trailers/2, send_trailers/3, - get_body_part/1, get_body_part/2 - ]). + get_body_part/1, get_body_part/2]). -include("lhttpc_types.hrl"). -include("lhttpc.hrl"). +%% @headerfile "lhttpc.hrl" %%============================================================================== %% Exported functions @@ -52,8 +57,7 @@ %%------------------------------------------------------------------------------ %% @hidden %%------------------------------------------------------------------------------ --spec start(normal | {takeover, node()} | {failover, node()}, any()) -> - {ok, pid()}. +-spec start(normal | {takeover, node()} | {failover, node()}, any()) -> {ok, pid()}. start(_, _) -> lhttpc_sup:start_link(). @@ -66,8 +70,6 @@ stop(_) -> %%------------------------------------------------------------------------------ -%% @spec () -> ok | {error, Reason} -%% Reason = term() %% @doc Start the application. %% This is a helper function that will call `application:start(lhttpc)' to %% allow the library to be started using the `-s' flag. @@ -82,8 +84,6 @@ start() -> application:start(lhttpc). %%------------------------------------------------------------------------------ -%% @spec () -> ok | {error, Reason} -%% Reason = term() %% @doc Stops the application. %% This is a helper function that will call `application:stop(lhttpc)'. %% @@ -95,57 +95,36 @@ stop() -> application:stop(lhttpc). %%------------------------------------------------------------------------------ -%% @spec (Name) -> {ok, Pid} | {error, Reason} -%% Name = atom() -%% Pid = pid() -%% Reason = term() -%% @doc Add a new named httpc_manager pool to the supervisor tree +%% @doc Create a new named httpc_manager pool. %% @end %%------------------------------------------------------------------------------ -spec add_pool(atom()) -> {ok, pid()} | {error, term()}. add_pool(Name) when is_atom(Name) -> {ok, ConnTimeout} = application:get_env(lhttpc, connection_timeout), {ok, PoolSize} = application:get_env(lhttpc, pool_size), - add_pool(Name, - ConnTimeout, - PoolSize). + add_pool(Name, ConnTimeout, PoolSize). %%------------------------------------------------------------------------------ -%% @doc Add a new httpc_manager to the supervisor tree +%% @doc Create a new named httpc_manager pool with the specified connection +%% timeout. %% @end %%------------------------------------------------------------------------------ -spec add_pool(atom(), non_neg_integer()) -> {ok, pid()} | {error, term()}. -add_pool(Name, ConnTimeout) when is_atom(Name), - is_integer(ConnTimeout), - ConnTimeout > 0 -> +add_pool(Name, ConnTimeout) when is_atom(Name), is_integer(ConnTimeout), ConnTimeout > 0 -> {ok, PoolSize} = application:get_env(lhttpc, pool_size), add_pool(Name, ConnTimeout, PoolSize). %%------------------------------------------------------------------------------ -%% @doc Add a new httpc_manager to the supervisor tree +%% @doc Create a new named httpc_manager pool with the specified connection +%% timeout and size. %% @end %%------------------------------------------------------------------------------ --spec add_pool(atom(), non_neg_integer(), poolsize()) -> - {ok, pid()} | {error, term()}. +-spec add_pool(atom(), non_neg_integer(), poolsize()) -> {ok, pid()} | {error, term()}. add_pool(Name, ConnTimeout, PoolSize) -> - ChildSpec = {Name, - {lhttpc_manager, start_link, [[{name, Name}, - {connection_timeout, ConnTimeout}, - {pool_size, PoolSize}]]}, - permanent, 10000, worker, [lhttpc_manager]}, - case supervisor:start_child(lhttpc_sup, ChildSpec) of - {error, {already_started, _Pid}} -> - {error, already_exists}; - {error, Error} -> - {error, Error}; - {ok, Pid} -> - {ok, Pid}; - {ok, Pid, _Info} -> - {ok, Pid} - end. + lhttpc_manager:new_pool(Name, ConnTimeout, PoolSize). %%------------------------------------------------------------------------------ -%% @doc Delete a pool +%% @doc Delete an existing pool %% @end %%------------------------------------------------------------------------------ -spec delete_pool(atom() | pid()) -> ok. @@ -155,26 +134,174 @@ delete_pool(PoolPid) when is_pid(PoolPid) -> delete_pool(PoolName) when is_atom(PoolName) -> case supervisor:terminate_child(lhttpc_sup, PoolName) of ok -> case supervisor:delete_child(lhttpc_sup, PoolName) of - ok -> ok; - {error, not_found} -> ok - end; + ok -> ok; + {error, not_found} -> ok + end; {error, Reason} -> {error, Reason} end. %%------------------------------------------------------------------------------ -%% @spec (URL, Method, Hdrs, Timeout) -> Result -%% URL = string() -%% Method = string() | atom() -%% Hdrs = [{Header, Value}] -%% Header = string() | binary() | atom() -%% Value = string() | binary() -%% Timeout = integer() | infinity -%% Result = {ok, {{StatusCode, ReasonPhrase}, Hdrs, ResponseBody}} -%% | {error, Reason} -%% StatusCode = integer() -%% ReasonPhrase = string() -%% ResponseBody = binary() -%% Reason = connection_closed | connect_timeout | timeout +%% @doc Starts a Client process and connects it to a Destination, which can be +%% either an URL or a tuple +%% `{Host, Port, Ssl}' +%% If the pool is provided whithin the options, it uses the pool to get an +%% existing connection or creates a new one managed by it. +%% This is intended to be able to create a client process that will generate many +%% requests to a given destination, keeping the process alive, using +%% {@link request_client/5}, {@link request_client/5} or {@link request_client/7}. +%% +%% The only relevant options to be included here (other options will be ignored) are: +%% +%% `{connect_timeout, Milliseconds}' specifies how many milliseconds the +%% client can spend trying to establish a connection to the server. This +%% doesn't affect the overall request timeout. However, if it's longer than +%% the overall timeout it will be ignored. Also note that the TCP layer my +%% choose to give up earlier than the connect timeout, in which case the +%% client will also give up. The default value is infinity, which means that +%% it will either give up when the TCP stack gives up, or when the overall +%% request timeout is reached. +%% +%% `{connect_options, Options}' specifies options to pass to the socket at +%% connect time. This makes it possible to specify both SSL options and +%% regular socket options, such as which IP/Port to connect from etc. +%% Some options must not be included here, namely the mode, `binary' +%% or `list', `{active, boolean()}', `{active, once}' or `{packet, Packet}'. +%% These options would confuse the client if they are included. +%% Please note that these options will only have an effect on *new* +%% connections, and it isn't possible for different requests +%% to the same host uses different options unless the connection is closed +%% between the requests. Using HTTP/1.0 or including the "Connection: close" +%% header would make the client close the connection after the first +%% response is received. +%% +%% `{use_cookies, UseCookies}' If set to true, the client will automatically +%% handle the cookies for the user. Considering that the Cookies get stored +%% in the client process state, the usage of this option only has effect when +%% using the request_client() functions, NOT when using standalone requests, +%% since in such case a new process is spawned each time. +%% +%% `{pool_options, PoolOptions}' This are the configuration options regarding the +%% pool of connections usage. If the `pool_ensure' option is true, the pool with +%% the given name in the `pool' option will be created and then used if it +%% does not exist. +%% The `pool_connection_timeout' specifies the time that the pool keeps a +%% connection open before it gets closed, `pool_max_size' specifies the number of +%% connections the pool can handle. +%% @end +%%------------------------------------------------------------------------------ +-spec connect_client(destination(), options()) -> {ok, pid()} | ignore | {error, term()}. +connect_client(Destination, Options) -> + lhttpc_client:start({Destination, Options}, []). + +%%------------------------------------------------------------------------------ +%% @doc Stops a Client process and closes the connection (if no pool is used) or +%% it returns the connection to the pool (if pool is used). +%% @end +%%------------------------------------------------------------------------------ +-spec disconnect_client(pid()) -> ok. +disconnect_client(Client) -> + lhttpc_client:stop(Client). + +%REQUESTS USING THE CLIENT + +%%------------------------------------------------------------------------------ +%% @doc Makes a request using a client already connected. +%% It can receive either a URL or a path. +%% Would be the same as calling {@link request_client/6} with an empty body. +%% @end +%%------------------------------------------------------------------------------ +-spec request_client(pid(), string(), method(), headers(), pos_timeout()) -> result(). +request_client(Client, PathOrUrl, Method, Hdrs, Timeout) -> + request_client(Client, PathOrUrl, Method, Hdrs, [], Timeout, []). + +%%------------------------------------------------------------------------------ +%% @doc Makes a request using a client already connected. +%% It can receive either a URL or a path. +%% %% Would be the same as calling {@link request_client/7} with no options. +%% @end +%%------------------------------------------------------------------------------ +-spec request_client(pid(), string(), method(), headers(), iodata(), pos_timeout()) -> result(). +request_client(Client, PathOrUrl, Method, Hdrs, Body, Timeout) -> + request_client(Client, PathOrUrl, Method, Hdrs, Body, Timeout, []). + +%%------------------------------------------------------------------------------ +%% @doc Makes a request using a client already connected. +%% It can receive either a URL or a path. It allows to add the body and specify +%% options. The only relevant options here are: +%% +%% `{send_retry, N}' specifies how many times the client should retry +%% sending a request if the connection is closed after the data has been +%% sent. The default value is `1'. If `{partial_upload, WindowSize}' +%% (see below) is specified, the client cannot retry after the first part +%% of the body has been sent since it doesn't keep the whole entitity body +%% in memory. +%% +%% `{partial_upload, WindowSize}' means that the request entity body will be +%% supplied in parts to the client by the calling process. The `WindowSize' +%% specifies how many parts can be sent to the process controlling the socket +%% before waiting for an acknowledgement. This is to create a kind of +%% internal flow control if the network is slow and the client process is +%% blocked by the TCP stack. Flow control is disabled if `WindowSize' is +%% `infinity'. If `WindowSize' is an integer, it must be >= 0. If partial +%% upload is specified and no `Content-Length' is specified in `Hdrs' the +%% client will use chunked transfer encoding to send the entity body. +%% If a content length is specified, this must be the total size of the entity +%% body. +%% The call to {@link request/6} will return `{ok, UploadState}'. The +%% `UploadState' is supposed to be used as the first argument to the {@link +%% send_body_part/2} or {@link send_body_part/3} functions to send body parts. +%% Partial upload is intended to avoid keeping large request bodies in +%% memory but can also be used when the complete size of the body isn't known +%% when the request is started. +%% +%% `{partial_download, PartialDownloadOptions}' means that the response body +%% will be supplied in parts by the client to the calling process. The partial +%% download option `{window_size, WindowSize}' specifies how many part will be +%% sent to the calling process before waiting for an acknowledgement. This is +%% to create a kind of internal flow control if the calling process is slow to +%% process the body part and the network and server are considerably faster. +%% Flow control is disabled if `WindowSize' is `infinity'. If `WindowSize' +%% is an integer it must be >=0. The partial download option `{part_size, +%% PartSize}' specifies the size the body parts should come in. Note however +%% that if the body size is not determinable (e.g entity body is termintated +%% by closing the socket) it will be delivered in pieces as it is read from +%% the wire. There is no caching of the body parts until the amount reaches +%% body size. If the body size is bounded (e.g `Content-Length' specified or +%% `Transfer-Encoding: chunked' specified) it will be delivered in `PartSize' +%% pieces. Note however that the last piece might be smaller than `PartSize'. +%% Size bounded entity bodies are handled the same way as unbounded ones if +%% `PartSize' is `infinity'. If `PartSize' is integer it must be >= 0. +%% If `{partial_download, PartialDownloadOptions}' is specified the +%% `ResponseBody' will be a `pid()' unless the response has no body +%% (for example in case of `HEAD' requests). In that case it will be be +%% `undefined'. The functions {@link get_body_part/1} and +%% {@link get_body_part/2} can be used to read body parts in the calling +%% process. +%% +%% `{proxy, ProxyUrl}' if this option is specified, a proxy server is used as +%% an intermediary for all communication with the destination server. The link +%% to the proxy server is established with the HTTP CONNECT method (RFC2817). +%% Example value: {proxy, "http://john:doe@myproxy.com:3128"} +%% +%% `{proxy_ssl_options, SslOptions}' this is a list of SSL options to use for +%% the SSL session created after the proxy connection is established. For a +%% list of all available options, please check OTP's ssl module manpage. +%% +%% @end +%%------------------------------------------------------------------------------ +-spec request_client(pid(), string(), method(), headers(), iodata(), + pos_timeout(), options()) -> result(). +request_client(Client, PathOrUrl, Method, Hdrs, Body, Timeout, Options) -> + verify_options(Options), + try + Reply = lhttpc_client:request(Client, PathOrUrl, Method, Hdrs, Body, Options, Timeout), + Reply + catch + exit:{timeout, _} -> + {error, timeout} + end. + +%%------------------------------------------------------------------------------ %% @doc Sends a request without a body. %% Would be the same as calling {@link request/5} with an empty body, %% `request(URL, Method, Hdrs, [], Timeout)' or @@ -187,20 +314,6 @@ request(URL, Method, Hdrs, Timeout) -> request(URL, Method, Hdrs, [], Timeout, []). %%------------------------------------------------------------------------------ -%% @spec (URL, Method, Hdrs, RequestBody, Timeout) -> Result -%% URL = string() -%% Method = string() | atom() -%% Hdrs = [{Header, Value}] -%% Header = string() | binary() | atom() -%% Value = string() | binary() -%% RequestBody = iodata() -%% Timeout = integer() | infinity -%% Result = {ok, {{StatusCode, ReasonPhrase}, Hdrs, ResponseBody}} -%% | {error, Reason} -%% StatusCode = integer() -%% ReasonPhrase = string() -%% ResponseBody = binary() -%% Reason = connection_closed | connect_timeout | timeout %% @doc Sends a request with a body. %% Would be the same as calling {@link request/6} with no options, %% `request(URL, Method, Hdrs, Body, Timeout, [])'. @@ -212,39 +325,6 @@ request(URL, Method, Hdrs, Body, Timeout) -> request(URL, Method, Hdrs, Body, Timeout, []). %%------------------------------------------------------------------------------ -%% @spec (URL, Method, Hdrs, RequestBody, Timeout, Options) -> Result -%% URL = string() -%% Method = string() | atom() -%% Hdrs = [{Header, Value}] -%% Header = string() | binary() | atom() -%% Value = string() | binary() -%% RequestBody = iodata() -%% Timeout = integer() | infinity -%% Options = [Option] -%% Option = {connect_timeout, Milliseconds | infinity} | -%% {connect_options, [ConnectOptions]} | -%% {send_retry, integer()} | -%% {partial_upload, WindowSize} | -%% {partial_download, PartialDownloadOptions} | -%% {proxy, ProxyUrl} | -%% {proxy_ssl_options, SslOptions} | -%% {pool, LhttcPool} -%% Milliseconds = integer() -%% ConnectOptions = term() -%% WindowSize = integer() | infinity -%% PartialDownloadOptions = [PartialDownloadOption] -%% PartialDowloadOption = {window_size, WindowSize} | -%% {part_size, PartSize} -%% ProxyUrl = string() -%% SslOptions = [any()] -%% LhttcPool = pid() | atom() -%% PartSize = integer() | infinity -%% Result = {ok, {{StatusCode, ReasonPhrase}, Hdrs, ResponseBody}} | -%% {ok, UploadState} | {error, Reason} -%% StatusCode = integer() -%% ReasonPhrase = string() -%% ResponseBody = binary() | pid() | undefined -%% Reason = connection_closed | connect_timeout | timeout %% @doc Sends a request with a body. %% Would be the same as calling
 %% #lhttpc_url{host = Host, port = Port, path = Path, is_ssl = Ssl} = lhttpc_lib:parse_url(URL),
@@ -256,19 +336,12 @@ request(URL, Method, Hdrs, Body, Timeout) ->
 %% @see request/9
 %% @end
 %%------------------------------------------------------------------------------
--spec request(string(), method(), headers(), iodata(),
-              pos_timeout(), options()) -> result().
+-spec request(string(), method(), headers(), iodata(), pos_timeout(), options()) -> result().
 request(URL, Method, Hdrs, Body, Timeout, Options) ->
-    #lhttpc_url{
-         host = Host,
-         port = Port,
-         path = Path,
-         is_ssl = Ssl,
-         user = User,
-         password = Passwd
-        } = lhttpc_lib:parse_url(URL),
+    #lhttpc_url{host = Host, port = Port, path = Path, is_ssl = Ssl,
+                user = User,password = Passwd} = lhttpc_lib:parse_url(URL),
     Headers = case User of
-        "" ->
+        [] ->
             Hdrs;
         _ ->
             Auth = "Basic " ++ binary_to_list(base64:encode(User ++ ":" ++ Passwd)),
@@ -277,43 +350,9 @@ request(URL, Method, Hdrs, Body, Timeout, Options) ->
     request(Host, Port, Ssl, Path, Method, Headers, Body, Timeout, Options).
 
 %%------------------------------------------------------------------------------
-%% @spec (Host, Port, Ssl, Path, Method, Hdrs, RequestBody, Timeout, Options) ->
-%%                                                                        Result
-%%   Host = string()
-%%   Port = integer()
-%%   Ssl = boolean()
-%%   Path = string()
-%%   Method = string() | atom()
-%%   Hdrs = [{Header, Value}]
-%%   Header = string() | binary() | atom()
-%%   Value = string() | binary()
-%%   RequestBody = iodata()
-%%   Timeout = integer() | infinity
-%%   Options = [Option]
-%%   Option = {connect_timeout, Milliseconds | infinity} |
-%%            {connect_options, [ConnectOptions]} |
-%%            {send_retry, integer()} |
-%%            {partial_upload, WindowSize} |
-%%            {partial_download, PartialDownloadOptions} |
-%%            {proxy, ProxyUrl} |
-%%            {proxy_ssl_options, SslOptions} |
-%%            {pool, LhttcPool}
-%%   Milliseconds = integer()
-%%   WindowSize = integer()
-%%   PartialDownloadOptions = [PartialDownloadOption]
-%%   PartialDowloadOption = {window_size, WindowSize} |
-%%                          {part_size, PartSize}
-%%   ProxyUrl = string()
-%%   SslOptions = [any()]
-%%   LhttcPool = pid() | atom()
-%%   PartSize = integer() | infinity
-%%   Result = {ok, {{StatusCode, ReasonPhrase}, Hdrs, ResponseBody}}
-%%          | {error, Reason}
-%%   StatusCode = integer()
-%%   ReasonPhrase = string()
-%%   ResponseBody = binary() | pid() | undefined
-%%   Reason = connection_closed | connect_timeout | timeout
-%% @doc Sends a request with a body.
+%% @doc Sends a request with a body. For this, a new client process is created,
+%% it creates a connection or takes it from a pool (depends of the options) and
+%% uses that client process to send the request. Then it stops the process.
 %%
 %% Instead of building and parsing URLs the target server is specified with
 %% a host, port, weither SSL should be used or not and a path on the server.
@@ -413,6 +452,12 @@ request(URL, Method, Hdrs, Body, Timeout, Options) ->
 %% {@link get_body_part/2} can be used to read body parts in the calling
 %% process.
 %%
+%% `{use_cookies, UseCookies}' If set to true, the client will automatically
+%% handle the cookies for the user. Considering that the Cookies get stored
+%% in the client process state, the usage of this option only has effect when
+%% using the request_client() functions, NOT when using standalone requests,
+%% since in such case a new process is spawned each time.
+%%
 %% `{proxy, ProxyUrl}' if this option is specified, a proxy server is used as
 %% an intermediary for all communication with the destination server. The link
 %% to the proxy server is established with the HTTP CONNECT method (RFC2817).
@@ -421,126 +466,79 @@ request(URL, Method, Hdrs, Body, Timeout, Options) ->
 %% `{proxy_ssl_options, SslOptions}' this is a list of SSL options to use for
 %% the SSL session created after the proxy connection is established. For a
 %% list of all available options, please check OTP's ssl module manpage.
+%%
+%% `{pool_options, PoolOptions}' This are the configuration options regarding the
+%% pool of connections usage. If the `pool_ensure' option is true, the pool with
+%% the given name in the `pool' option will be created and then used if it
+%% does not exist.
+%% The `pool_connection_timeout' specifies the time that the pool keeps a
+%% connection open before it gets closed, `pool_max_size' specifies the number of
+%% connections the pool can handle.
 %% @end
 %%------------------------------------------------------------------------------
--spec request(string(), port_num(), boolean(), string(), method(),
-    headers(), iodata(), pos_timeout(), options()) -> result().
+-spec request(host(), port_num(), boolean(), string(), method(),
+              headers(), iodata(), pos_timeout(), options()) -> result().
 request(Host, Port, Ssl, Path, Method, Hdrs, Body, Timeout, Options) ->
     verify_options(Options),
-    Args = [self(), Host, Port, Ssl, Path, Method, Hdrs, Body, Options],
-    Pid = spawn_link(lhttpc_client, request, Args),
-    receive
-        {response, Pid, R} ->
-            R;
-        {'EXIT', Pid, Reason} ->
-            % This could happen if the process we're running in traps exits
-            % and the client process exits due to some exit signal being
-            % sent to it. Very unlikely though
-            {error, Reason}
-    after Timeout ->
-            kill_client(Pid)
+    case connect_client({Host, Port, Ssl}, Options) of
+        {ok, Client} ->
+            try
+                Reply = lhttpc_client:request(Client, Path, Method, Hdrs, Body, Options, Timeout),
+                disconnect_client(Client),
+                Reply
+            catch
+                exit:{timeout, _} ->
+                    disconnect_client(Client),
+                    {error, timeout}
+            end;
+        {error, {timeout, _Reason}} ->
+            {error, connection_timeout};
+        {error, _Reason} = Error ->
+            Error
     end.
 
 %%------------------------------------------------------------------------------
-%% @spec (UploadState :: UploadState, BodyPart :: BodyPart) -> Result
-%%   BodyPart = iodata() | binary()
-%%   Timeout = integer() | infinity
-%%   Result = {error, Reason} | UploadState
-%%   Reason = connection_closed | connect_timeout | timeout
 %% @doc Sends a body part to an ongoing request when
-%% `{partial_upload, WindowSize}' is used. The default timeout, `infinity'
-%% will be used. Notice that if `WindowSize' is infinity, this call will never
-%% block.
-%% Would be the same as calling
-%% `send_body_part(UploadState, BodyPart, infinity)'.
+%% `{partial_upload, true}' is used. The default timeout, `infinity'
+%% will be used. Would be the same as calling
+%% `send_body_part(Client, BodyPart, infinity)'.
 %% @end
 %%------------------------------------------------------------------------------
--spec send_body_part(upload_state(), bodypart()) -> result().
-send_body_part({Pid, Window}, IoList) ->
-    send_body_part({Pid, Window}, IoList, infinity).
+-spec send_body_part(pid(), bodypart()) -> result().
+send_body_part(Client, BodyPart) ->
+    send_body_part(Client, BodyPart, infinity).
 
 %%------------------------------------------------------------------------------
-%% @spec (UploadState :: UploadState, BodyPart :: BodyPart, Timeout) -> Result
-%%   BodyPart = iodata() | binary()
-%%   Timeout = integer() | infinity
-%%   Result = {error, Reason} | UploadState
-%%   Reason = connection_closed | connect_timeout | timeout
 %% @doc Sends a body part to an ongoing request when
-%% `{partial_upload, WindowSize}' is used.
+%% `{partial_upload, true}' is used.
 %% `Timeout' is the timeout for the request in milliseconds.
 %%
-%% If the window size reaches 0 the call will block for at maximum Timeout
-%% milliseconds. If there is no acknowledgement received during that time the
-%% the request is cancelled and `{error, timeout}' is returned.
-%%
-%% As long as the window size is larger than 0 the function will return
-%% immediately after sending the body part to the request handling process.
-%%
 %% The `BodyPart' `http_eob' signals an end of the entity body, the request
 %% is considered sent and the response will be read from the socket. If
 %% there is no response within `Timeout' milliseconds, the request is
 %% canceled and `{error, timeout}' is returned.
 %% @end
 %%------------------------------------------------------------------------------
--spec send_body_part(upload_state(), bodypart(), timeout()) -> result().
-send_body_part({Pid, _Window}, http_eob, Timeout) when is_pid(Pid) ->
-    Pid ! {body_part, self(), http_eob},
-    read_response(Pid, Timeout);
-send_body_part({Pid, 0}, IoList, Timeout) when is_pid(Pid) ->
-    receive
-        {ack, Pid} ->
-            send_body_part({Pid, 1}, IoList, Timeout);
-        {response, Pid, R} ->
-            R;
-        {'EXIT', Pid, Reason} ->
-            {error, Reason}
-    after Timeout ->
-        kill_client(Pid)
-    end;
-send_body_part({Pid, Window}, IoList, _Timeout) when Window > 0, is_pid(Pid) ->
-                                                     % atom > 0 =:= true
-    Pid ! {body_part, self(), IoList},
-    receive
-        {ack, Pid} ->
-            {ok, {Pid, Window}};
-        {response, Pid, R} ->
-            R;
-        {'EXIT', Pid, Reason} ->
-            {error, Reason}
-    after 0 ->
-        {ok, {Pid, lhttpc_lib:dec(Window)}}
-    end.
+-spec send_body_part(pid(), bodypart(), timeout()) -> result().
+send_body_part(Client, BodyPart, Timeout)  ->
+    lhttpc_client:send_body_part(Client, BodyPart, Timeout).
 
 %%------------------------------------------------------------------------------
-%% @spec (UploadState :: UploadState, Trailers) -> Result
-%%   Header = string() | binary() | atom()
-%%   Value = string() | binary()
-%%   Result = {ok, {{StatusCode, ReasonPhrase}, Hdrs, ResponseBody}}
-%%            | {error, Reason}
-%%   Reason = connection_closed | connect_timeout | timeout
 %% @doc Sends trailers to an ongoing request when `{partial_upload,
-%% WindowSize}' is used and no `Content-Length' was specified. The default
+%% true}' is used and no `Content-Length' was specified. The default
 %% timout `infinity' will be used. Plase note that after this the request is
 %% considered complete and the response will be read from the socket.
 %% Would be the same as calling
-%% `send_trailers(UploadState, BodyPart, infinity)'.
+%% `send_trailers(Client, BodyPart, infinity)'.
 %% @end
 %%------------------------------------------------------------------------------
--spec send_trailers({pid(), window_size()}, headers()) -> result().
-send_trailers({Pid, Window}, Trailers) ->
-    send_trailers({Pid, Window}, Trailers, infinity).
+-spec send_trailers(pid(), headers()) -> result().
+send_trailers(Client, Trailers) ->
+    lhttpc_client:send_trailers(Client, Trailers, infinity).
 
 %%------------------------------------------------------------------------------
-%% @spec (UploadState :: UploadState, Trailers, Timeout) -> Result
-%%   Trailers = [{Header, Value}]
-%%   Header = string() | binary() | atom()
-%%   Value = string() | binary()
-%%   Timeout = integer() | infinity
-%%   Result = {ok, {{StatusCode, ReasonPhrase}, Hdrs, ResponseBody}}
-%%            | {error, Reason}
-%%   Reason = connection_closed | connect_timeout | timeout
 %% @doc Sends trailers to an ongoing request when
-%% `{partial_upload, WindowSize}' is used and no `Content-Length' was
+%% `{partial_upload, true}' is used and no `Content-Length' was
 %% specified.
 %% `Timeout' is the timeout for sending the trailers and reading the
 %% response in milliseconds.
@@ -552,19 +550,11 @@ send_trailers({Pid, Window}, Trailers) ->
 %% returned.
 %% @end
 %%------------------------------------------------------------------------------
--spec send_trailers({pid(), window_size()}, headers(), timeout()) -> result().
-send_trailers({Pid, _Window}, Trailers, Timeout)
-        when is_list(Trailers), is_pid(Pid) ->
-    Pid ! {trailers, self(), Trailers},
-    read_response(Pid, Timeout).
+-spec send_trailers(pid(), headers(), timeout()) -> result().
+send_trailers(Client, Trailers, Timeout) when is_list(Trailers), is_pid(Client) ->
+    lhttpc_client:send_trailers(Client, Trailers, Timeout).
 
 %%------------------------------------------------------------------------------
-%% @spec (HTTPClient :: pid()) -> Result
-%%   Result = {ok, BodyPart} | {ok, {http_eob, Trailers}}
-%%   BodyPart = binary()
-%%   Trailers = [{Header, Value}]
-%%   Header = string() | binary() | atom()
-%%   Value = string() | binary()
 %% @doc Reads a body part from an ongoing response when
 %% `{partial_download, PartialDownloadOptions}' is used. The default timeout,
 %% `infinity' will be used.
@@ -572,19 +562,11 @@ send_trailers({Pid, _Window}, Trailers, Timeout)
 %% `get_body_part(HTTPClient, infinity)'.
 %% @end
 %%------------------------------------------------------------------------------
--spec get_body_part(pid()) -> {ok, binary()} |
-                       {ok, {http_eob, headers()}} | {error, term()}.
+-spec get_body_part(pid()) -> {ok, binary()} | {ok, {http_eob, headers()}} | {error, term()}.
 get_body_part(Pid) ->
     get_body_part(Pid, infinity).
 
 %%------------------------------------------------------------------------------
-%% @spec (HTTPClient :: pid(), Timeout:: Timeout) -> Result
-%%   Timeout = integer() | infinity
-%%   Result = {ok, BodyPart} | {ok, {http_eob, Trailers}}
-%%   BodyPart = binary()
-%%   Trailers = [{Header, Value}]
-%%   Header = string() | binary() | atom()
-%%   Value = string() | binary()
 %% @doc Reads a body part from an ongoing response when
 %% `{partial_download, PartialDownloadOptions}' is used.
 %% `Timeout' is the timeout for reading the next body part in milliseconds.
@@ -595,19 +577,9 @@ get_body_part(Pid) ->
 %% @end
 %%------------------------------------------------------------------------------
 -spec get_body_part(pid(), timeout()) -> {ok, binary()} |
-                           {ok, {http_eob, headers()}} | {error, term()}.
-get_body_part(Pid, Timeout) ->
-    receive
-        {body_part, Pid, Bin} ->
-            Pid ! {ack, self()},
-            {ok, Bin};
-        {http_eob, Pid, Trailers} ->
-            {ok, {http_eob, Trailers}};
-        {error, Pid, Reason} ->
-            {error, Reason}
-    after Timeout ->
-        kill_client(Pid)
-    end.
+                                         {ok, {http_eob, headers()}} | {error, term()}.
+get_body_part(Client, Timeout) ->
+    lhttpc_client:get_body_part(Client, Timeout).
 
 %%==============================================================================
 %% Internal functions
@@ -616,39 +588,7 @@ get_body_part(Pid, Timeout) ->
 %%------------------------------------------------------------------------------
 %% @private
 %%------------------------------------------------------------------------------
--spec read_response(pid(), timeout()) -> result().
-read_response(Pid, Timeout) ->
-    receive
-        {ack, Pid} ->
-            read_response(Pid, Timeout);
-        {response, Pid, R} ->
-            R;
-        {'EXIT', Pid, Reason} ->
-            {error, Reason}
-    after Timeout ->
-        kill_client(Pid)
-    end.
-
-%%------------------------------------------------------------------------------
-%% @private
-%%------------------------------------------------------------------------------
--spec kill_client(pid()) -> any() | {error, any()}.
-kill_client(Pid) ->
-    Monitor = erlang:monitor(process, Pid),
-    unlink(Pid), % or we'll kill ourself :O
-    exit(Pid, timeout),
-    receive
-        {response, Pid, R} ->
-            erlang:demonitor(Monitor, [flush]),
-            R;
-        {'DOWN', _, process, Pid, Reason}  ->
-            {error, Reason}
-    end.
-
-%%------------------------------------------------------------------------------
-%% @private
-%%------------------------------------------------------------------------------
--spec verify_options(options()) -> ok.
+-spec verify_options(options()) -> ok | any().
 verify_options([{send_retry, N} | Options]) when is_integer(N), N >= 0 ->
     verify_options(Options);
 verify_options([{connect_timeout, infinity} | Options]) ->
@@ -656,8 +596,8 @@ verify_options([{connect_timeout, infinity} | Options]) ->
 verify_options([{connect_timeout, MS} | Options])
         when is_integer(MS), MS >= 0 ->
     verify_options(Options);
-verify_options([{partial_upload, WindowSize} | Options])
-        when is_integer(WindowSize), WindowSize >= 0 ->
+verify_options([{partial_upload, Bool} | Options])
+        when is_boolean(Bool) ->
     verify_options(Options);
 verify_options([{partial_upload, infinity} | Options])  ->
     verify_options(Options);
@@ -671,22 +611,34 @@ verify_options([{proxy, List} | Options]) when is_list(List) ->
     verify_options(Options);
 verify_options([{proxy_ssl_options, List} | Options]) when is_list(List) ->
     verify_options(Options);
-verify_options([{pool, PidOrName} | Options])
-        when is_pid(PidOrName); is_atom(PidOrName) ->
+verify_options([{pool_options, PoolOptions} | Options]) ->
+    ok = verify_pool_options(PoolOptions),
     verify_options(Options);
-verify_options([{pool_ensure, Bool} | Options])
+verify_options([Option | _Rest]) ->
+    erlang:error({bad_option, Option});
+verify_options([]) ->
+    ok.
+
+%%------------------------------------------------------------------------------
+%% @private
+%%------------------------------------------------------------------------------
+-spec verify_pool_options(pool_options()) -> ok | no_return().
+verify_pool_options([{pool, PidOrName} | Options])
+        when is_pid(PidOrName); is_atom(PidOrName) ->
+    verify_pool_options(Options);
+verify_pool_options([{pool_ensure, Bool} | Options])
         when is_boolean(Bool) ->
-    verify_options(Options);
-verify_options([{pool_connection_timeout, Size} | Options])
+    verify_pool_options(Options);
+verify_pool_options([{pool_connection_timeout, Size} | Options])
         when is_integer(Size) ->
-    verify_options(Options);
-verify_options([{pool_max_size, Size} | Options])
+    verify_pool_options(Options);
+verify_pool_options([{pool_max_size, Size} | Options])
         when is_integer(Size) orelse
-             Size =:= infinity->
-    verify_options(Options);
-verify_options([Option | _Rest]) ->
+        Size =:= infinity->
+    verify_pool_options(Options);
+verify_pool_options([Option | _Rest]) ->
     erlang:error({bad_option, Option});
-verify_options([]) ->
+verify_pool_options([]) ->
     ok.
 
 %%------------------------------------------------------------------------------
@@ -703,6 +655,16 @@ verify_partial_download([{part_size, Size} | Options]) when
     verify_partial_download(Options);
 verify_partial_download([{part_size, infinity} | Options]) ->
     verify_partial_download(Options);
+%verify_partial_download([{recv_proc, PidOrName} | Options])  when
+%      is_atom(PidOrName) ->
+%    Pid = whereis(PidOrName),
+%    is_alive(Pid) =:= true,
+%    verify_partial_download(Options);
+%verify_partial_download([{recv_proc, PidOrName} | Options]) when
+%      is_alive(Pid) ->
+%    verify_partial_download(Options);
+verify_partial_download([{recv_proc, _PidOrName} | Options]) ->
+    verify_partial_download(Options);
 verify_partial_download([Option | _Options]) ->
     erlang:error({bad_option, {partial_download, Option}});
 verify_partial_download([]) ->
diff --git a/src/lhttpc_client.erl b/src/lhttpc_client.erl
index 3a159120..8b38b9fa 100644
--- a/src/lhttpc_client.erl
+++ b/src/lhttpc_client.erl
@@ -1,5 +1,5 @@
 %%% ----------------------------------------------------------------------------
-%%% Copyright (c) 2009, Erlang Training and Consulting Ltd.
+%%% Copyright (c) 2009-2013, Erlang Solutions Ltd.
 %%% All rights reserved.
 %%%
 %%% Redistribution and use in source and binary forms, with or without
@@ -9,14 +9,14 @@
 %%%    * Redistributions in binary form must reproduce the above copyright
 %%%      notice, this list of conditions and the following disclaimer in the
 %%%      documentation and/or other materials provided with the distribution.
-%%%    * Neither the name of Erlang Training and Consulting Ltd. nor the
+%%%    * Neither the name of Erlang Solutions Ltd. nor the
 %%%      names of its contributors may be used to endorse or promote products
 %%%      derived from this software without specific prior written permission.
 %%%
-%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS''
+%%% THIS SOFTWARE IS PROVIDED BY Erlang Solutions Ltd. ''AS IS''
 %%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 %%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE
+%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Solutions Ltd. BE
 %%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 %%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 %%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
@@ -27,90 +27,130 @@
 %%------------------------------------------------------------------------------
 %%% @private
 %%% @author Oscar Hellström 
+%%% @author Diana Parra Corbacho 
+%%% @author Ramon Lastres Guerrero 
 %%% @doc This module implements the HTTP request handling. This should normally
 %%% not be called directly since it should be spawned by the lhttpc module.
 %%% @end
 %%------------------------------------------------------------------------------
 -module(lhttpc_client).
 
--export([request/9]).
+%exported functions
+-export([start_link/2,
+         start/2,
+         request/7,
+         send_body_part/3,
+         send_trailers/3,
+         get_body_part/2,
+         stop/1]).
+
+%% gen_server callbacks
+-export([init/1,
+         handle_call/3,
+         handle_cast/2,
+         handle_info/2,
+         terminate/2,
+         code_change/3]).
 
 -include("lhttpc_types.hrl").
 -include("lhttpc.hrl").
 
+-define(HTTP_LINE_END, "\r\n").
 -define(CONNECTION_HDR(HDRS, DEFAULT),
-    string:to_lower(lhttpc_lib:header_value("connection", HDRS, DEFAULT))).
+        lhttpc_lib:to_lower(lhttpc_lib:header_value("connection", HDRS, DEFAULT))).
 
 -record(client_state, {
         host :: string(),
         port = 80 :: port_num(),
         ssl = false :: boolean(),
-        method :: string(),
-        request :: iolist(),
-        request_headers :: headers(),
+        pool = undefined,
+        pool_options,
         socket,
-        connect_timeout = infinity :: timeout(),
+        connect_timeout = 'infinity' :: timeout(),
         connect_options = [] :: [any()],
-        attempts :: integer(),
-        requester :: pid(),
+        %% next fields are specific to particular requests
+        request :: iolist() | undefined,
+        method :: string(),
+        request_headers :: headers(),
+        requester,
+        cookies = [] :: [#lhttpc_cookie{}],
+        use_cookies = false :: boolean(),
         partial_upload = false :: boolean(),
         chunked_upload = false :: boolean(),
-        upload_window :: non_neg_integer() | infinity,
         partial_download = false :: boolean(),
         download_window = infinity :: timeout(),
+        download_proc :: pid(),
         part_size :: non_neg_integer() | infinity,
         %% in case of infinity we read whatever data we can get from
         %% the wire at that point or in case of chunked one chunk
+        attempts = 0 :: integer(),
+        download_info :: {term(), term()},
+        body_length = undefined :: {'fixed_length', non_neg_integer()} |
+                                    'undefined' | 'chunked' | 'infinite',
         proxy :: undefined | #lhttpc_url{},
         proxy_ssl_options = [] :: [any()],
         proxy_setup = false :: boolean()
-    }).
+        }).
 
 %%==============================================================================
 %% Exported functions
 %%==============================================================================
+start(Args, Options) ->
+    gen_server:start(?MODULE, Args, Options).
+
+start_link(Args, Options) ->
+    gen_server:start_link(?MODULE, Args, Options).
+
+send_body_part(Client, Part, Timeout) ->
+    gen_server:call(Client, {send_body_part, Part}, Timeout).
+
+send_trailers(Client, Trailers, Timeout) ->
+    gen_server:call(Client, {send_trailers, Trailers}, Timeout).
+
+get_body_part(Client, Timeout) ->
+    gen_server:call(Client, get_body_part, Timeout).
+
+stop(Client) ->
+    gen_server:cast(Client, stop).
 
 %%------------------------------------------------------------------------------
-%% @spec (From, Host, Port, Ssl, Path, Method, Hdrs, RequestBody, Options) -> ok
-%%    From = pid()
-%%    Host = string()
-%%    Port = integer()
-%%    Ssl = boolean()
-%%    Method = atom() | string()
-%%    Hdrs = [Header]
-%%    Header = {string() | atom(), string()}
-%%    Body = iolist()
-%%    Options = [Option]
-%%    Option = {connect_timeout, Milliseconds}
 %% @doc
 %% @end
 %%------------------------------------------------------------------------------
--spec request(pid(), string(), port_num(), boolean(), string(),
-        method(), headers(), iolist(), options()) -> ok.
-request(From, Host, Port, Ssl, Path, Method, Hdrs, Body, Options) ->
-    Result = try
-        execute(From, Host, Port, Ssl, Path, Method, Hdrs, Body, Options)
-    catch
-        Reason ->
-            {response, self(), {error, Reason}};
-        error:closed ->
-            {response, self(), {error, connection_closed}};
-        error:Reason ->
-            Stack = erlang:get_stacktrace(),
-            {response, self(), {error, {Reason, Stack}}}
-    end,
-    case Result of
-        {response, _, {ok, {no_return, _}}} -> ok;
-        _Else                               -> From ! Result
-    end,
-    % Don't send back {'EXIT', self(), normal} if the process
-    % calling us is trapping exits
-    unlink(From),
-    ok.
+-spec request(pid(), string(), method(), headers(), iolist(), options(), integer()) -> result().
+request(Client, Path, Method, Hdrs, Body, Options, Timeout) ->
+    gen_server:call(Client,
+                    {request, Path, Method, Hdrs, Body, Options}, Timeout).
 
-%%==============================================================================
-%% Internal functions
-%%==============================================================================
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+init({Destination, Options}) ->
+    PoolOptions = proplists:get_value(pool_options, Options, []),
+    Pool = proplists:get_value(pool, PoolOptions),
+    ConnectTimeout = proplists:get_value(connect_timeout, Options, infinity),
+    ConnectOptions = proplists:get_value(connect_options, Options, []),
+    UseCookies = proplists:get_value(use_cookies, Options, false),
+    {Host, Port, Ssl} = case Destination of
+        {H, P, S} ->
+            {H, P, S};
+        URL ->
+            #lhttpc_url{host = H, port = P,
+                        is_ssl = S} = lhttpc_lib:parse_url(URL),
+            {H, P, S}
+    end,
+    State = #client_state{host = Host, port = Port, ssl = Ssl, pool = Pool,
+                          connect_timeout = ConnectTimeout,
+                          connect_options = ConnectOptions,
+                          use_cookies = UseCookies,
+                          pool_options = PoolOptions},
+    %% Get a socket for the pool or exit
+    case connect_socket(State) of
+        {ok, NewState} ->
+            {ok, NewState};
+        {{error, Reason}, _} ->
+            {stop, Reason}
+    end.
 
 %%------------------------------------------------------------------------------
 %% @doc This function fills in the Client record used in the requests and obtains
@@ -118,9 +158,11 @@ request(From, Host, Port, Ssl, Path, Method, Hdrs, Body, Options) ->
 %% socket used is new, it also makes the pool gen_server its controlling process.
 %% @end
 %%------------------------------------------------------------------------------
-execute(From, Host, Port, Ssl, Path, Method, Hdrs, Body, Options) ->
-    UploadWindowSize = proplists:get_value(partial_upload, Options),
-    PartialUpload = proplists:is_defined(partial_upload, Options),
+handle_call({request, PathOrUrl, Method, Hdrs, Body, Options}, From,
+            State = #client_state{ssl = Ssl, host = ClientHost, port = ClientPort,
+                                  socket = Socket, cookies = Cookies,
+                                  use_cookies = UseCookies}) ->
+    PartialUpload = proplists:get_value(partial_upload, Options, false),
     PartialDownload = proplists:is_defined(partial_download, Options),
     PartialDownloadOptions = proplists:get_value(partial_download, Options, []),
     NormalizedMethod = lhttpc_lib:normalize_method(Method),
@@ -134,54 +176,165 @@ execute(From, Host, Port, Ssl, Path, Method, Hdrs, Body, Options) ->
         ProxyUrl when is_list(ProxyUrl) ->
             lhttpc_lib:parse_url(ProxyUrl)
     end,
-    {ChunkedUpload, Request} = lhttpc_lib:format_request(Path, NormalizedMethod,
-        Hdrs, Host, Port, Body, PartialUpload),
-    %SocketRequest = {socket, self(), Host, Port, Ssl},
-    Pool = proplists:get_value(pool, Options, whereis(lhttpc_manager)),
-    %% Get a socket for the pool or exit
-    %Socket = lhttpc_manager:ensure_call(Pool, SocketRequest, Options),
-    Socket = lhttpc_manager:ensure_call(Pool, self(), Host, Port, Ssl, Options),
-    State = #client_state{
-        host = Host,
-        port = Port,
-        ssl = Ssl,
-        method = NormalizedMethod,
-        request = Request,
-        requester = From,
-        request_headers = Hdrs,
-        socket = Socket,
-        connect_timeout = proplists:get_value(connect_timeout, Options,
-            infinity),
-        connect_options = proplists:get_value(connect_options, Options, []),
-        attempts = 1 + proplists:get_value(send_retry, Options, 1),
-        partial_upload = PartialUpload,
-        upload_window = UploadWindowSize,
-        chunked_upload = ChunkedUpload,
-        partial_download = PartialDownload,
-        download_window = proplists:get_value(window_size,
-            PartialDownloadOptions, infinity),
-        part_size = proplists:get_value(part_size,
-            PartialDownloadOptions, infinity),
-        proxy = Proxy,
-        proxy_setup = (Socket =/= undefined),
-        proxy_ssl_options = proplists:get_value(proxy_ssl_options, Options, [])
-    },
-    Response = case send_request(State) of
-        {R, undefined} ->
-            {ok, R};
-        {R, NewSocket} ->
-            % The socket we ended up doing the request over is returned
-            % here, it might be the same as Socket, but we don't know.
-            % I've noticed that we don't want to give send sockets that we
-            % can't change the controlling process for to the manager. This
-            % really shouldn't fail, but it could do if:
-            % * The socket was closed remotely already
-            % * Due to an error in this module (returning dead sockets for
-            %   instance)
-            ok = lhttpc_manager:client_done(Pool, Host, Port, Ssl, NewSocket),
-            {ok, R}
-    end,
-    {response, self(), Response}.
+    {FinalPath, FinalHeaders, Host, Port} =
+        url_extract(PathOrUrl, Hdrs, ClientHost, ClientPort),
+    case {Host, Port} =:= {ClientHost, ClientPort} of
+        true ->
+            {ChunkedUpload, Request} =
+                lhttpc_lib:format_request(FinalPath, NormalizedMethod,
+                    FinalHeaders, Host, Port, Body, PartialUpload,
+                {UseCookies, Cookies}),
+            NewState =
+                    State#client_state{
+                        method = NormalizedMethod,
+                        request = Request,
+                        requester = From,
+                        request_headers = Hdrs,
+                        attempts = proplists:get_value(send_retry, Options, 1),
+                        partial_upload = PartialUpload,
+                        chunked_upload = ChunkedUpload,
+                        partial_download = PartialDownload,
+                        download_window =
+                            proplists:get_value(window_size, PartialDownloadOptions,
+                                                infinity),
+                        download_proc =
+                            proplists:get_value(recv_proc, PartialDownloadOptions,
+                                                infinity),
+                        part_size =
+                            proplists:get_value(part_size, PartialDownloadOptions,
+                                                infinity),
+                        proxy = Proxy,
+                        proxy_setup = (Socket /= undefined),
+                        proxy_ssl_options =
+                            proplists:get_value(proxy_ssl_options, Options, [])},
+            send_request(NewState);
+        _ ->
+            {reply, {error, host_or_port_different_to_connected}, State}
+    end;
+handle_call(_Msg, _From, #client_state{request = undefined} = State) ->
+    {reply, {error, no_pending_request}, State};
+handle_call({send_body_part, _}, _From, State = #client_state{partial_upload = false}) ->
+    {reply, {error, no_partial_upload}, State};
+handle_call({trailers, _}, _From, State = #client_state{partial_upload = false}) ->
+    {reply, {error, no_partial_upload}, State};
+handle_call(get_body_part, _From, State = #client_state{partial_download = false}) ->
+    {reply, {error, no_partial_download}, State};
+handle_call(_Msg, _From, #client_state{socket = undefined} = State) ->
+    {reply, {error, connection_closed}, State#client_state{request = undefined}};
+handle_call({send_trailers, Trailers}, _From, State) ->
+    case send_trailers(State, Trailers) of
+        {ok, NewState} ->
+            read_response(NewState);
+        {Error, NewState} ->
+            {reply, Error, NewState}
+    end;
+handle_call({send_body_part, http_eob}, From, State) ->
+    case send_body_part(State, http_eob) of
+        {ok, NewState} ->
+            read_response(NewState#client_state{requester = From});
+        {Error, NewState} ->
+            {reply, Error, NewState}
+    end;
+handle_call({send_body_part, Data}, From, State) ->
+    gen_server:reply(From, ok),
+    {_Reply, NewState} = send_body_part(State, Data),
+    {noreply, NewState};
+%We send the parts to the specified Pid.
+handle_call(get_body_part, From, State) ->
+    gen_server:reply(From, ok),
+    {noreply, read_partial_body(State)}.
+
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling cast messages
+%% @end
+%%--------------------------------------------------------------------
+handle_cast(stop, State) ->
+    {stop, normal, State};
+handle_cast(_Msg, State) ->
+    {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling all non call/cast messages
+%%
+%% @spec handle_info(Info, State) -> {noreply, State} |
+%%                                   {noreply, State, Timeout} |
+%%                                   {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_info(_Info, State) ->
+    {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any
+%% necessary cleaning up. When it returns, the gen_server terminates
+%% with Reason. The return value is ignored.
+%%
+%% @end
+%%--------------------------------------------------------------------
+terminate(_Reason, State = #client_state{pool = Pool, host = Host, ssl = Ssl,
+                                         socket = Socket, port = Port}) ->
+    case Socket of
+        undefined ->
+            ok;
+        _ ->
+            case Pool of
+                undefined ->
+                    close_socket(State),
+                    ok;
+                _ ->
+                    %return the control of the socket to the pool.
+                    lhttpc_manager:client_done(Pool, Host, Port, Ssl, Socket)
+            end
+    end.
+
+
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Convert process state when code is changed
+%% @end
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+    {ok, State}.
+
+%%==============================================================================
+%% Internal functions
+%%==============================================================================
+
+%%------------------------------------------------------------------------------
+%% @private
+%%------------------------------------------------------------------------------
+url_extract(PathOrUrl, Hdrs, ClientHost, ClientPort) ->
+    try #lhttpc_url{host = UrlHost, port = UrlPort, path = Path, is_ssl = _Ssl,
+                    user = User, password = Passwd} = lhttpc_lib:parse_url(PathOrUrl),
+        Headers = case User of
+            [] ->
+                Hdrs;
+            _ ->
+                Auth = "Basic " ++ binary_to_list(base64:encode(User ++ ":" ++ Passwd)),
+                lists:keystore("Authorization", 1, Hdrs, {"Authorization", Auth})
+        end,
+        {Path, Headers, UrlHost, UrlPort}
+    catch %if parse_url crashes we assume it is a path.
+        _:_ ->
+            {PathOrUrl, Hdrs, ClientHost, ClientPort}
+    end.
+
+%%------------------------------------------------------------------------------
+%% @private
+%%------------------------------------------------------------------------------
+send_body_part(State = #client_state{socket = Socket, ssl = Ssl}, BodyPart) ->
+    Data = encode_body_part(State, BodyPart),
+    check_send_result(State, lhttpc_sock:send(Socket, Data, Ssl)).
 
 %%------------------------------------------------------------------------------
 %% @private
@@ -189,105 +342,70 @@ execute(From, Host, Port, Ssl, Path, Method, Hdrs, Body, Options) ->
 %% handles the proxy connection.
 %% @end
 %%------------------------------------------------------------------------------
-send_request(#client_state{attempts = 0}) ->
-    % Don't try again if the number of allowed attempts is 0.
-    throw(connection_closed);
-%we need a socket.
+send_request(#client_state{attempts = 0} = State) ->
+    {reply, {error, connection_closed}, State#client_state{request = undefined}};
 send_request(#client_state{socket = undefined} = State) ->
-    {Host, Port, Ssl} = request_first_destination(State),
-    Timeout = State#client_state.connect_timeout,
-    ConnectOptions0 = State#client_state.connect_options,
-    ConnectOptions = case (not lists:member(inet, ConnectOptions0)) andalso
-                         (not lists:member(inet6, ConnectOptions0)) andalso
-                         is_ipv6_host(Host) of
-        true ->
-            [inet6 | ConnectOptions0];
-        false ->
-            ConnectOptions0
-    end,
-    SocketOptions = [binary, {packet, http}, {active, false} | ConnectOptions],
-    try lhttpc_sock:connect(Host, Port, SocketOptions, Timeout, Ssl) of
-        {ok, Socket} ->
-            send_request(State#client_state{socket = Socket});
-        {error, etimedout} ->
-            % TCP stack decided to give up
-            throw(connect_timeout);
-        {error, timeout} ->
-            throw(connect_timeout);
-        {error, 'record overflow'} ->
-            throw(ssl_error);
-        {error, Reason} ->
-            erlang:error(Reason)
-    catch
-        exit:{{{badmatch, {error, {asn1, _}}}, _}, _} ->
-            throw(ssl_decode_error);
-        Type:Error ->
-                    error_logger:error_msg("Socket connection error: ~p ~p, ~p",
-                                           [Type, Error, erlang:get_stacktrace()])
+    % if we dont get a keep alive from the previous request, the socket is undefined.
+    case connect_socket(State) of
+        {ok, NewState} ->
+            send_request(NewState);
+        {Error, NewState} ->
+            {reply, Error, NewState}
     end;
-send_request(#client_state{proxy = #lhttpc_url{}, proxy_setup = false} = State) ->
-% use a proxy.
-    #lhttpc_url{
-        user = User,
-        password = Passwd,
-        is_ssl = Ssl
-    } = State#client_state.proxy,
-    #client_state{
-        host = DestHost,
-        port = Port,
-        socket = Socket
-    } = State,
+send_request(#client_state{proxy = #lhttpc_url{}, proxy_setup = false,
+                           host = DestHost, port = Port, socket = Socket} = State) ->
+    %% use a proxy.
+    #lhttpc_url{user = User, password = Passwd, is_ssl = Ssl} = State#client_state.proxy,
     Host = case inet_parse:address(DestHost) of
         {ok, {_, _, _, _, _, _, _, _}} ->
-            % IPv6 address literals are enclosed by square brackets (RFC2732)
+            %% IPv6 address literals are enclosed by square brackets (RFC2732)
             [$[, DestHost, $], $:, integer_to_list(Port)];
         _ ->
             [DestHost, $:, integer_to_list(Port)]
     end,
     ConnectRequest = [
-        "CONNECT ", Host, " HTTP/1.1\r\n",
-        "Host: ", Host, "\r\n",
-        case User of
-            "" ->
-                "";
-            _ ->
-                ["Proxy-Authorization: Basic ",
-                    base64:encode(User ++ ":" ++ Passwd), "\r\n"]
-        end,
-        "\r\n"
-    ],
+            "CONNECT ", Host, " HTTP/1.1", ?HTTP_LINE_END,
+            "Host: ", Host, ?HTTP_LINE_END,
+            case User of
+                [] ->
+                    [];
+                _ ->
+                    ["Proxy-Authorization: Basic ",
+                     base64:encode(User ++ ":" ++ Passwd), ?HTTP_LINE_END]
+            end,
+            ?HTTP_LINE_END],
     case lhttpc_sock:send(Socket, ConnectRequest, Ssl) of
         ok ->
-            read_proxy_connect_response(State, nil, nil);
+            {Reply, NewState} = read_proxy_connect_response(State, nil, nil),
+            {reply, Reply, NewState};
         {error, closed} ->
-            lhttpc_sock:close(Socket, Ssl),
-            throw(proxy_connection_closed);
-        {error, Reason} ->
-            lhttpc_sock:close(Socket, Ssl),
-            erlang:error(Reason)
+            close_socket(State),
+            {reply, {error, proxy_connection_closed},
+             State#client_state{socket = undefined, request = undefined}};
+        {error, _Reason} ->
+            close_socket(State),
+            {reply, {error, proxy_connection_closed},
+             State#client_state{socket = undefined, request = undefined}}
     end;
-send_request(State) ->
-%already have socket
-    Socket = State#client_state.socket,
-    Ssl = State#client_state.ssl,
-    Request = State#client_state.request,
+send_request(#client_state{socket = Socket, ssl = Ssl, request = Request,
+                           attempts = Attempts} = State) ->
+    %% no proxy
     case lhttpc_sock:send(Socket, Request, Ssl) of
         ok ->
             if
-                % {partial_upload, WindowSize} is used.
-                State#client_state.partial_upload     -> partial_upload(State);
-                not State#client_state.partial_upload -> read_response(State)
+                %% {partial_upload, WindowSize} is used.
+                State#client_state.partial_upload     ->
+                    {reply, {ok, partial_upload}, State#client_state{attempts = 0}};
+                not State#client_state.partial_upload ->
+                    read_response(State)
             end;
         {error, closed} ->
-            lhttpc_sock:close(Socket, Ssl),
-            NewState = State#client_state{
-                socket = undefined,
-                attempts = State#client_state.attempts - 1
-            },
-            send_request(NewState);
-        {error, Reason} ->
-            lhttpc_sock:close(Socket, Ssl),
-            erlang:error(Reason)
+            close_socket(State),
+            send_request(State#client_state{socket = undefined, attempts = Attempts - 1});
+        {error, _Reason} ->
+            close_socket(State),
+            {reply, {error, connection_closed}, State#client_state{socket = undefined,
+                                                                   request = undefined}}
     end.
 
 %%------------------------------------------------------------------------------
@@ -310,15 +428,15 @@ read_proxy_connect_response(State, StatusCode, StatusText) ->
         {ok, {http_header, _, _Name, _, _Value}} ->
             read_proxy_connect_response(State, StatusCode, StatusText);
         {ok, http_eoh} when StatusCode >= 100, StatusCode =< 199 ->
-            % RFC 2616, section 10.1:
-            % A client MUST be prepared to accept one or more
-            % 1xx status responses prior to a regular
-            % response, even if the client does not expect a
-            % 100 (Continue) status message. Unexpected 1xx
-            % status responses MAY be ignored by a user agent.
+            %% RFC 2616, section 10.1:
+            %% A client MUST be prepared to accept one or more
+            %% 1xx status responses prior to a regular
+            %% response, even if the client does not expect a
+            %% 100 (Continue) status message. Unexpected 1xx
+            %% status responses MAY be ignored by a user agent.
             read_proxy_connect_response(State, nil, nil);
         {ok, http_eoh} when StatusCode >= 200, StatusCode < 300 ->
-            % RFC2817, any 2xx code means success.
+            %% RFC2817, any 2xx code means success.
             ConnectOptions = State#client_state.connect_options,
             SslOptions = State#client_state.proxy_ssl_options,
             Timeout = State#client_state.connect_timeout,
@@ -326,87 +444,52 @@ read_proxy_connect_response(State, StatusCode, StatusText) ->
                 {ok, SslSocket} ->
                     State#client_state{socket = SslSocket, proxy_setup = true};
                 {error, Reason} ->
-                    lhttpc_sock:close(Socket, ProxyIsSsl),
-                    erlang:error({proxy_connection_failed, Reason})
+                    close_socket(State),
+                    {{error, {proxy_connection_failed, Reason}}, State}
             end,
             send_request(State2);
         {ok, http_eoh} ->
-            throw({proxy_connection_refused, StatusCode, StatusText});
+            {{error, {proxy_connection_refused, StatusCode, StatusText}}, State};
         {error, closed} ->
-            lhttpc_sock:close(Socket, ProxyIsSsl),
-            throw(proxy_connection_closed);
+            close_socket(State),
+            {{error, proxy_connection_closed},
+             State#client_state{socket = undefined, request = undefined}};
         {error, Reason} ->
-            erlang:error({proxy_connection_failed, Reason})
+            {{error, {proxy_connection_failed, Reason}},
+             State#client_state{request = undefined}}
     end.
 
-
-%%------------------------------------------------------------------------------
-%% @private
-%% @doc Called when {partial_upload, WindowSize} is used. The user can send
-%% messages using functions in lhttpc module
-%% @end
-%%------------------------------------------------------------------------------
-partial_upload(State) ->
-    Response = {ok, {self(), State#client_state.upload_window}},
-    State#client_state.requester ! {response, self(), Response},
-    partial_upload_loop(State#client_state{attempts = 1, request = undefined}).
-
-%%------------------------------------------------------------------------------
-%% @private
-%%------------------------------------------------------------------------------
-partial_upload_loop(State = #client_state{requester = Pid}) ->
-    receive
-        {trailers, Pid, Trailers} ->
-            send_trailers(State, Trailers),
-            read_response(State);
-        {body_part, Pid, http_eob} ->
-            send_body_part(State, http_eob),
-            read_response(State);
-        {body_part, Pid, Data} ->
-            send_body_part(State, Data),
-            Pid ! {ack, self()},
-            partial_upload_loop(State)
-    end.
-
-%%------------------------------------------------------------------------------
-%% @private
-%%------------------------------------------------------------------------------
-send_body_part(State = #client_state{socket = Socket, ssl = Ssl}, BodyPart) ->
-    Data = encode_body_part(State, BodyPart),
-    check_send_result(State, lhttpc_sock:send(Socket, Data, Ssl)).
-
 %%------------------------------------------------------------------------------
 %% @private
 %%------------------------------------------------------------------------------
-send_trailers(State = #client_state{chunked_upload = true}, Trailers) ->
-    Socket = State#client_state.socket,
-    Ssl = State#client_state.ssl,
-    Data = [<<"0\r\n">>, lhttpc_lib:format_hdrs(Trailers)],
-    check_send_result(State, lhttpc_sock:send(Socket, Data, Ssl));
-send_trailers(#client_state{chunked_upload = false}, _Trailers) ->
-    erlang:error(trailers_not_allowed).
+send_trailers(#client_state{chunked_upload = true, socket = Socket, ssl= Ssl} = State, Trailers) ->
+    Data = list_to_binary("0" ++ ?HTTP_LINE_END),
+    Data2 = [Data, lhttpc_lib:format_hdrs(Trailers)],
+    check_send_result(State, lhttpc_sock:send(Socket, Data2, Ssl));
+send_trailers(#client_state{chunked_upload = false} = State, _Trailers) ->
+    {{error, trailers_not_allowed}, State}.
 
 %%------------------------------------------------------------------------------
 %% @private
 %%------------------------------------------------------------------------------
 encode_body_part(#client_state{chunked_upload = true}, http_eob) ->
-    <<"0\r\n\r\n">>; % We don't send trailers after http_eob
+    list_to_binary("0" ++ ?HTTP_LINE_END ++ ?HTTP_LINE_END);
 encode_body_part(#client_state{chunked_upload = false}, http_eob) ->
     <<>>;
 encode_body_part(#client_state{chunked_upload = true}, Data) ->
     Size = list_to_binary(erlang:integer_to_list(iolist_size(Data), 16)),
-    [Size, <<"\r\n">>, Data, <<"\r\n">>];
+    [Size, <>, Data, <>];
 encode_body_part(#client_state{chunked_upload = false}, Data) ->
     Data.
 
 %%------------------------------------------------------------------------------
 %% @private
 %%------------------------------------------------------------------------------
-check_send_result(_State, ok) ->
-    ok;
-check_send_result(#client_state{socket = Sock, ssl = Ssl}, {error, Reason}) ->
-    lhttpc_sock:close(Sock, Ssl),
-    throw(Reason).
+check_send_result(State, ok) ->
+    {ok, State};
+check_send_result(State, Error) ->
+    close_socket(State),
+    {Error, State#client_state{socket = undefined, request = undefined}}.
 
 %%------------------------------------------------------------------------------
 %% @private
@@ -421,11 +504,11 @@ read_response(#client_state{socket = Socket, ssl = Ssl} = State) ->
 %% @doc @TODO This does not handle redirects at the moment.
 %% @end
 %%------------------------------------------------------------------------------
--spec read_response(#client_state{}, {integer(), integer()} | 'nil', http_status(),
-       any()) -> {any(), socket()} | no_return().
-read_response(State, Vsn, {StatusCode, _} = Status, Hdrs) ->
-    Socket = State#client_state.socket,
-    Ssl = State#client_state.ssl,
+-spec read_response(#client_state{}, {integer(), integer()} | 'nil', http_status(), any()) ->
+    {any(), socket()} | no_return().
+read_response(#client_state{socket = Socket, ssl = Ssl, use_cookies = UseCookies,
+                            request_headers = ReqHdrs, cookies = Cookies} = State,
+              Vsn, {StatusCode, _} = Status, Hdrs) ->
     case lhttpc_sock:recv(Socket, Ssl) of
         {ok, {http_response, NewVsn, NewStatusCode, Reason}} ->
             NewStatus = {NewStatusCode, Reason},
@@ -443,27 +526,41 @@ read_response(State, Vsn, {StatusCode, _} = Status, Hdrs) ->
             read_response(State, nil, {nil, nil}, []);
         {ok, http_eoh} ->
             lhttpc_sock:setopts(Socket, [{packet, raw}], Ssl),
-            Response = handle_response_body(State, Vsn, Status, Hdrs),
-            NewHdrs = element(2, Response),
-            ReqHdrs = State#client_state.request_headers,
-            NewSocket = maybe_close_socket(Socket, Ssl, Vsn, ReqHdrs, NewHdrs),
-            {Response, NewSocket};
+            {Reply, NewState} = handle_response_body(State, Vsn, Status, Hdrs),
+            case Reply of
+                noreply ->
+                    %when partial_download is used. We do not close the socket.
+                    {noreply, NewState#client_state{socket = Socket}};
+                {error, Reason} ->
+                    {reply, {error, Reason}, NewState#client_state{socket = undefined}};
+                _ ->
+                    NewHdrs = element(2, Reply),
+                    FinalCookies = case UseCookies of
+                        true ->
+                            lhttpc_lib:update_cookies(NewHdrs, Cookies);
+                        _ ->
+                            []
+                    end,
+                    NewSocket = maybe_close_socket(State, Vsn, ReqHdrs, NewHdrs),
+                    {reply, {ok, Reply}, NewState#client_state{socket = NewSocket,
+                                                               request = undefined,
+                                                               cookies = FinalCookies}}
+            end;
         {error, closed} ->
+            %% TODO does it work for partial uploads? I think should return an error
+
             % Either we only noticed that the socket was closed after we
             % sent the request, the server closed it just after we put
-            % the request on the wire or the server has some issues and is
+            % the request on the wire or the server has some isses and is
             % closing connections without sending responses.
             % If this the first attempt to send the request, we will try again.
-            lhttpc_sock:close(Socket, Ssl),
-            NewState = State#client_state{
-                socket = undefined,
-                attempts = State#client_state.attempts - 1
-            },
+            close_socket(State),
+            NewState = State#client_state{socket = undefined},
             send_request(NewState);
         {ok, {http_error, _} = Reason} ->
-            erlang:error(Reason);
+            {reply, {error, Reason}, State#client_state{request = undefined}};
         {error, Reason} ->
-            erlang:error(Reason)
+            {reply, {error, Reason}, State#client_state{request = undefined}}
     end.
 
 %%------------------------------------------------------------------------------
@@ -471,34 +568,37 @@ read_response(State, Vsn, {StatusCode, _} = Status, Hdrs) ->
 %% @doc Handles the reading of the response body.
 %% @end
 %%------------------------------------------------------------------------------
--spec handle_response_body(#client_state{}, {integer(), integer()},
-                http_status(), headers()) -> {http_status(), headers(), body()} |
-                                             {http_status(), headers()}.
+-spec handle_response_body(#client_state{}, {integer(), integer()}, http_status(), headers()) ->
+    {http_status(), headers(), body()} | {http_status(), headers()} |
+    {'noreply', any()} | {{'error', any()}, any()}.
 handle_response_body(#client_state{partial_download = false} = State, Vsn,
-        Status, Hdrs) ->
-%when {partial_download, PartialDownloadOptions} option is NOT used.
+                     Status, Hdrs) ->
+    %when {partial_download, PartialDownloadOptions} option is NOT used.
     Socket = State#client_state.socket,
     Ssl = State#client_state.ssl,
     Method = State#client_state.method,
-    {Body, NewHdrs} = case has_body(Method, element(1, Status), Hdrs) of
-                          true  -> read_body(Vsn, Hdrs, Ssl, Socket, body_type(Hdrs));
-                          false -> {<<>>, Hdrs}
-                      end,
-    {Status, NewHdrs, Body};
-handle_response_body(#client_state{partial_download = true} = State, Vsn,
-        Status, Hdrs) ->
-%when {partial_download, PartialDownloadOptions} option is used.
-    Method = State#client_state.method,
+    Reply = case has_body(Method, element(1, Status), Hdrs) of
+        true  -> read_body(Vsn, Hdrs, Ssl, Socket, body_type(Hdrs));
+        false -> {<<>>, Hdrs}
+    end,
+    case Reply of
+        {error, Reason} ->
+            {{error, Reason}, State};
+        {Body, NewHdrs} ->
+            {{Status, NewHdrs, Body}, State}
+    end;
+handle_response_body(#client_state{partial_download = true, requester =
+                                   Requester, method = Method} = State, Vsn, Status, Hdrs) ->
+    %when {partial_download, PartialDownloadOptions} option is used.
     case has_body(Method, element(1, Status), Hdrs) of
         true ->
-            Response = {ok, {Status, Hdrs, self()}},
-            State#client_state.requester ! {response, self(), Response},
-            MonRef = erlang:monitor(process, State#client_state.requester),
-            Res = read_partial_body(State, Vsn, Hdrs, body_type(Hdrs)),
-            erlang:demonitor(MonRef, [flush]),
-            Res;
+            Response = {ok, {Status, Hdrs, partial_download}},
+            gen_server:reply(Requester, Response),
+            NewState = State#client_state{download_info = {Vsn, Hdrs},
+                                          body_length = body_type(Hdrs)},
+            {noreply, read_partial_body(NewState)};
         false ->
-            {Status, Hdrs, undefined}
+            {{Status, Hdrs, undefined}, State}
     end.
 
 %%------------------------------------------------------------------------------
@@ -539,9 +639,9 @@ has_body(_, _, _) ->
 body_type(Hdrs) ->
     case lhttpc_lib:header_value("content-length", Hdrs) of
         undefined ->
-            TransferEncoding = string:to_lower(
-                lhttpc_lib:header_value("transfer-encoding", Hdrs, "undefined")
-            ),
+            TransferEncoding = lhttpc_lib:to_lower(
+                    lhttpc_lib:header_value("transfer-encoding", Hdrs, "undefined")
+                    ),
             case TransferEncoding of
                 "chunked" -> chunked;
                 _         -> infinite
@@ -549,22 +649,6 @@ body_type(Hdrs) ->
         ContentLength ->
             {fixed_length, list_to_integer(ContentLength)}
     end.
-
-%%------------------------------------------------------------------------------
-%%% @private
-%%% @doc Called when {partial_download, PartialDownloadOptions} option is used.
-%%% @end
-%%------------------------------------------------------------------------------
-read_partial_body(State, _Vsn, Hdrs, chunked) ->
-    Window = State#client_state.download_window,
-    read_partial_chunked_body(State, Hdrs, Window, 0, [], 0);
-read_partial_body(State, Vsn, Hdrs, infinite) ->
-    check_infinite_response(Vsn, Hdrs),
-    read_partial_infinite_body(State, Hdrs, State#client_state.download_window);
-read_partial_body(State, _Vsn, Hdrs, {fixed_length, ContentLength}) ->
-    read_partial_finite_body(State, Hdrs, ContentLength,
-        State#client_state.download_window).
-
 %%------------------------------------------------------------------------------
 %%% @private
 %%% @doc Called when {partial_download, PartialDownloadOptions} option is NOT used.
@@ -580,52 +664,50 @@ read_body(_Vsn, Hdrs, Ssl, Socket, {fixed_length, ContentLength}) ->
 
 %%------------------------------------------------------------------------------
 %%% @private
+%%% @doc Called when {partial_download, PartialDownloadOptions} option is used.
+%%% @end
 %%------------------------------------------------------------------------------
-read_partial_finite_body(State = #client_state{}, Hdrs, 0, _Window) ->
-    reply_end_of_body(State, [], Hdrs);
-read_partial_finite_body(State = #client_state{requester = To}, Hdrs,
-        ContentLength, 0) ->
-    receive
-        {ack, To} ->
-            read_partial_finite_body(State, Hdrs, ContentLength, 1);
-        {'DOWN', _, process, To, _} ->
-            exit(normal)
-    end;
-read_partial_finite_body(State, Hdrs, ContentLength, Window) when Window >= 0->
+read_partial_body(State=#client_state{body_length = chunked}) ->
+    Window = State#client_state.download_window,
+    read_partial_chunked_body(State, Window, 0, [], 0);
+read_partial_body(State=#client_state{download_info = {Vsn, Hdrs}, body_length = infinite}) ->
+    check_infinite_response(Vsn, Hdrs),
+    read_partial_infinite_body(State, State#client_state.download_window);
+read_partial_body(State=#client_state{body_length = {fixed_length, ContentLength}}) ->
+    read_partial_finite_body(State, ContentLength, State#client_state.download_window).
+
+%%------------------------------------------------------------------------------
+%%% @private
+%%------------------------------------------------------------------------------
+read_partial_finite_body(State , 0, _Window) ->
+    reply_end_of_body(State, []),
+    State#client_state{request = undefined};
+read_partial_finite_body(#client_state{download_proc = To} = State, ContentLength, 0) ->
+    %finished the window, reply to ask for another call to get_body_part
+    To ! {body_part, window_finished},
+    State#client_state{body_length = {fixed_length, ContentLength}};
+read_partial_finite_body(State, ContentLength, Window) when Window > 0->
     case read_body_part(State, ContentLength) of
         {ok, Bin} ->
-            State#client_state.requester ! {body_part, self(), Bin},
-            To = State#client_state.requester,
-            receive
-                {ack, To} ->
-                    Length = ContentLength - iolist_size(Bin),
-                    read_partial_finite_body(State, Hdrs, Length, Window);
-                {'DOWN', _, process, To, _} ->
-                    exit(normal)
-            after 0 ->
-                    Length = ContentLength - iolist_size(Bin),
-                    read_partial_finite_body(State, Hdrs, Length, lhttpc_lib:dec(Window))
-            end;
+            State#client_state.download_proc ! {body_part, Bin},
+            Length = ContentLength - iolist_size(Bin),
+            read_partial_finite_body(State, Length, lhttpc_lib:dec(Window));
         {error, Reason} ->
-            State#client_state.requester ! {error, self(), Reason},
-            exit(normal)
+            State#client_state.download_proc ! {body_part_error, Reason},
+            close_socket(State),
+            State#client_state{request = undefined, socket = undefined}
     end.
 
 %%------------------------------------------------------------------------------
 %%% @private
 %%------------------------------------------------------------------------------
-read_body_part(#client_state{part_size = infinity} = State, _ContentLength) ->
-    lhttpc_sock:recv(State#client_state.socket, State#client_state.ssl);
-read_body_part(#client_state{part_size = PartSize} = State, ContentLength)
-        when PartSize =< ContentLength ->
-    Socket = State#client_state.socket,
-    Ssl = State#client_state.ssl,
-    PartSize = State#client_state.part_size,
+read_body_part(#client_state{part_size = infinity, socket = Socket, ssl = Ssl}, _ContentLength) ->
+    lhttpc_sock:recv(Socket, Ssl);
+read_body_part(#client_state{socket = Socket, ssl = Ssl, part_size = PartSize},
+               ContentLength) when PartSize =< ContentLength ->
     lhttpc_sock:recv(Socket, PartSize, Ssl);
-read_body_part(#client_state{part_size = PartSize} = State, ContentLength)
-        when PartSize > ContentLength ->
-    Socket = State#client_state.socket,
-    Ssl = State#client_state.ssl,
+read_body_part(#client_state{socket = Socket, ssl = Ssl, part_size = PartSize},
+               ContentLength) when PartSize > ContentLength ->
     lhttpc_sock:recv(Socket, ContentLength, Ssl).
 
 %%------------------------------------------------------------------------------
@@ -636,50 +718,51 @@ read_length(Hdrs, Ssl, Socket, Length) ->
         {ok, Data} ->
             {Data, Hdrs};
         {error, Reason} ->
-            erlang:error(Reason)
+            lhttpc_sock:close(Socket, Ssl),
+            {error, Reason}
     end.
 
 %%------------------------------------------------------------------------------
 %%% @private
 %%------------------------------------------------------------------------------
-read_partial_chunked_body(State, Hdrs, Window, BufferSize, Buffer, 0) ->
-    Socket = State#client_state.socket,
-    Ssl = State#client_state.ssl,
-    PartSize = State#client_state.part_size,
+read_partial_chunked_body(#client_state{download_proc = Pid} = State, 0, 0, _Buffer, 0) ->
+    %we ask for another call to get_body_part
+    Pid ! {body_part, http_eob},
+    State;
+read_partial_chunked_body(#client_state{download_info = {_Vsn, Hdrs}, socket = Socket,
+                                        ssl = Ssl, part_size = PartSize} = State,
+                          Window, BufferSize, Buffer, 0) ->
     case read_chunk_size(Socket, Ssl) of
         0 ->
             reply_chunked_part(State, Buffer, Window),
-            {Trailers, NewHdrs} = read_trailers(Socket, Ssl, [], Hdrs),
-            reply_end_of_body(State, Trailers, NewHdrs);
+            {Trailers, _NewHdrs} = read_trailers(Socket, Ssl, [], Hdrs),
+            reply_end_of_body(State, Trailers),
+            State#client_state{request = undefined};
         ChunkSize when PartSize =:= infinity ->
             Chunk = read_chunk(Socket, Ssl, ChunkSize),
             NewWindow = reply_chunked_part(State, [Chunk | Buffer], Window),
-            read_partial_chunked_body(State, Hdrs, NewWindow, 0, [], 0);
+            read_partial_chunked_body(State, NewWindow, 0, [], 0);
         ChunkSize when BufferSize + ChunkSize >= PartSize ->
             {Chunk, RemSize} = read_partial_chunk(Socket, Ssl,
-                PartSize - BufferSize, ChunkSize),
+                                                  PartSize - BufferSize, ChunkSize),
             NewWindow = reply_chunked_part(State, [Chunk | Buffer], Window),
-            read_partial_chunked_body(State, Hdrs, NewWindow, 0, [], RemSize);
+            read_partial_chunked_body(State, NewWindow, 0, [], RemSize);
         ChunkSize ->
             Chunk = read_chunk(Socket, Ssl, ChunkSize),
-            read_partial_chunked_body(State, Hdrs, Window,
-                BufferSize + ChunkSize, [Chunk | Buffer], 0)
+            read_partial_chunked_body(State, Window,
+                                      BufferSize + ChunkSize, [Chunk | Buffer], 0)
     end;
-read_partial_chunked_body(State, Hdrs, Window, BufferSize, Buffer, RemSize) ->
-    Socket = State#client_state.socket,
-    Ssl = State#client_state.ssl,
-    PartSize = State#client_state.part_size,
+read_partial_chunked_body(#client_state{socket = Socket, ssl = Ssl, part_size = PartSize} = State,
+                          Window, BufferSize, Buffer, RemSize) ->
     if
         BufferSize + RemSize >= PartSize ->
-            {Chunk, NewRemSize} =
-                read_partial_chunk(Socket, Ssl, PartSize - BufferSize, RemSize),
+            {Chunk, NewRemSize} = read_partial_chunk(Socket, Ssl,
+                                                     PartSize - BufferSize, RemSize),
             NewWindow = reply_chunked_part(State, [Chunk | Buffer], Window),
-            read_partial_chunked_body(State, Hdrs, NewWindow, 0, [],
-                NewRemSize);
+            read_partial_chunked_body(State, NewWindow, 0, [], NewRemSize);
         BufferSize + RemSize < PartSize ->
             Chunk = read_chunk(Socket, Ssl, RemSize),
-            read_partial_chunked_body(State, Hdrs, Window, BufferSize + RemSize,
-                [Chunk | Buffer], 0)
+            read_partial_chunked_body(State, Window, BufferSize + RemSize, [Chunk | Buffer], 0)
     end.
 
 %%------------------------------------------------------------------------------
@@ -699,21 +782,9 @@ read_chunk_size(Socket, Ssl) ->
 %%------------------------------------------------------------------------------
 reply_chunked_part(_State, [], Window) ->
     Window;
-reply_chunked_part(State = #client_state{requester = Pid}, Buff, 0) ->
-    receive
-        {ack, Pid} ->
-            reply_chunked_part(State, Buff, 1);
-        {'DOWN', _, process, Pid, _} ->
-            exit(normal)
-    end;
-reply_chunked_part(#client_state{requester = Pid}, Buffer, Window) ->
-    Pid ! {body_part, self(), list_to_binary(lists:reverse(Buffer))},
-    receive
-        {ack, Pid} ->  Window;
-        {'DOWN', _, process, Pid, _} -> exit(normal)
-    after 0 ->
-        lhttpc_lib:dec(Window)
-    end.
+reply_chunked_part(#client_state{download_proc = Pid}, Buffer, Window) ->
+    Pid ! {body_part, list_to_binary(lists:reverse(Buffer))},
+    lhttpc_lib:dec(Window).
 
 %%------------------------------------------------------------------------------
 %%% @private
@@ -740,7 +811,7 @@ chunk_size(Bin) ->
 %%------------------------------------------------------------------------------
 chunk_size(<<$;, _/binary>>, Chars) ->
     Chars;
-chunk_size(<<"\r\n", _/binary>>, Chars) ->
+chunk_size(<>, Chars) ->
     Chars;
 chunk_size(<<$\s, Binary/binary>>, Chars) ->
     %% Facebook's HTTP server returns a chunk size like "6  \r\n"
@@ -759,7 +830,7 @@ read_partial_chunk(Socket, Ssl, Size, ChunkSize) ->
         {ok, Chunk} ->
             {Chunk, ChunkSize - Size};
         {error, Reason} ->
-            erlang:error(Reason)
+            {error, Reason}
     end.
 
 %%------------------------------------------------------------------------------
@@ -768,19 +839,19 @@ read_partial_chunk(Socket, Ssl, Size, ChunkSize) ->
 read_chunk(Socket, Ssl, Size) ->
     lhttpc_sock:setopts(Socket, [{packet, raw}], Ssl),
     case lhttpc_sock:recv(Socket, Size + 2, Ssl) of
-        {ok, <>} ->
+        {ok, <>} ->
             Chunk;
         {ok, Data} ->
-            erlang:error({invalid_chunk, Data});
+            {error, {invalid_chunk, Data}};
         {error, Reason} ->
-            erlang:error(Reason)
+            {error, Reason}
     end.
 
 %%------------------------------------------------------------------------------
 %% @private
 %%------------------------------------------------------------------------------
 -spec read_trailers(socket(), boolean(), any(), any()) ->
-                           {any(), any()} | no_return().
+    {any(), any()} | no_return().
 read_trailers(Socket, Ssl, Trailers, Hdrs) ->
     lhttpc_sock:setopts(Socket, [{packet, httph}], Ssl),
     case lhttpc_sock:recv(Socket, Ssl) of
@@ -796,35 +867,25 @@ read_trailers(Socket, Ssl, Trailers, Hdrs) ->
 %%------------------------------------------------------------------------------
 %% @private
 %%------------------------------------------------------------------------------
--spec reply_end_of_body(#client_state{}, any(), any()) -> {'no_return', any()}.
-reply_end_of_body(#client_state{requester = Requester}, Trailers, Hdrs) ->
-    Requester ! {http_eob, self(), Trailers},
-    {no_return, Hdrs}.
+%-spec reply_end_of_body(#client_state{}, any(), any()) -> 'no_return'.
+reply_end_of_body(#client_state{download_proc = To}, Trailers) ->
+    To ! {http_eob, Trailers}.
 
 %%------------------------------------------------------------------------------
 %% @private
 %%------------------------------------------------------------------------------
-read_partial_infinite_body(State = #client_state{requester = To}, Hdrs, 0) ->
-    receive
-        {ack, To} ->
-            read_partial_infinite_body(State, Hdrs, 1);
-        {'DOWN', _, process, To, _} ->
-            exit(normal)
-    end;
-read_partial_infinite_body(State = #client_state{requester = To}, Hdrs, Window)
+read_partial_infinite_body(#client_state{download_proc = Pid} = State, 0) ->
+    Pid ! {body_part, window_finished},
+    State;
+read_partial_infinite_body(#client_state{download_proc = Pid} = State, Window)
         when Window >= 0 ->
     case read_infinite_body_part(State) of
-        http_eob -> reply_end_of_body(State, [], Hdrs);
+        http_eob ->
+            reply_end_of_body(State, []),
+            State#client_state{request = undefined};
         Bin ->
-            State#client_state.requester ! {body_part, self(), Bin},
-            receive
-                {ack, To} ->
-                    read_partial_infinite_body(State, Hdrs, Window);
-                {'DOWN', _, process, To, _} ->
-                    exit(normal)
-            after 0 ->
-                read_partial_infinite_body(State, Hdrs, lhttpc_lib:dec(Window))
-            end
+            Pid ! {body_part, Bin},
+            read_partial_infinite_body(State, lhttpc_lib:dec(Window))
     end.
 
 %%------------------------------------------------------------------------------
@@ -846,13 +907,13 @@ read_infinite_body_part(#client_state{socket = Socket, ssl = Ssl}) ->
 %%------------------------------------------------------------------------------
 check_infinite_response({1, Minor}, Hdrs) when Minor >= 1 ->
     HdrValue = lhttpc_lib:header_value("connection", Hdrs, "keep-alive"),
-    case string:to_lower(HdrValue) of
+    case lhttpc_lib:to_lower(HdrValue) of
         "close" -> ok;
         _       -> erlang:error(no_content_length)
     end;
 check_infinite_response(_, Hdrs) ->
     HdrValue = lhttpc_lib:header_value("connection", Hdrs, "close"),
-    case string:to_lower(HdrValue) of
+    case lhttpc_lib:to_lower(HdrValue) of
         "keep-alive" -> erlang:error(no_content_length);
         _            -> ok
     end.
@@ -861,7 +922,7 @@ check_infinite_response(_, Hdrs) ->
 %% @private
 %%------------------------------------------------------------------------------
 -spec read_infinite_body(socket(), headers(), boolean()) ->
-                        {binary(), headers()} | no_return().
+    {binary(), headers()} | no_return().
 read_infinite_body(Socket, Hdrs, Ssl) ->
     read_until_closed(Socket, <<>>, Hdrs, Ssl).
 
@@ -869,7 +930,7 @@ read_infinite_body(Socket, Hdrs, Ssl) ->
 %% @private
 %%------------------------------------------------------------------------------
 -spec read_until_closed(socket(), binary(), any(), boolean()) ->
-                        {binary(), any()} | no_return().
+    {binary(), any()} | no_return().
 read_until_closed(Socket, Acc, Hdrs, Ssl) ->
     case lhttpc_sock:recv(Socket, Ssl) of
         {ok, Body} ->
@@ -884,24 +945,25 @@ read_until_closed(Socket, Acc, Hdrs, Ssl) ->
 %%------------------------------------------------------------------------------
 %% @private
 %%------------------------------------------------------------------------------
-maybe_close_socket(Socket, Ssl, {1, Minor}, ReqHdrs, RespHdrs) when Minor >= 1->
+maybe_close_socket(#client_state{socket = Socket} = State, {1, Minor},
+                   ReqHdrs, RespHdrs) when Minor >= 1->
     ClientConnection = ?CONNECTION_HDR(ReqHdrs, "keep-alive"),
     ServerConnection = ?CONNECTION_HDR(RespHdrs, "keep-alive"),
     if
-        ClientConnection =:= "close"; ServerConnection =:= "close" ->
-            lhttpc_sock:close(Socket, Ssl),
+        ClientConnection == "close"; ServerConnection == "close" ->
+            close_socket(State),
             undefined;
-        ClientConnection =/= "close", ServerConnection =/= "close" ->
+        ClientConnection /= "close", ServerConnection /= "close" ->
             Socket
     end;
-maybe_close_socket(Socket, Ssl, _, ReqHdrs, RespHdrs) ->
+maybe_close_socket(#client_state{socket = Socket} = State, _, ReqHdrs, RespHdrs) ->
     ClientConnection = ?CONNECTION_HDR(ReqHdrs, "keep-alive"),
     ServerConnection = ?CONNECTION_HDR(RespHdrs, "close"),
     if
-        ClientConnection =:= "close"; ServerConnection =/= "keep-alive" ->
-            lhttpc_sock:close(Socket, Ssl),
+        ClientConnection == "close"; ServerConnection /= "keep-alive" ->
+            close_socket(State),
             undefined;
-        ClientConnection =/= "close", ServerConnection =:= "keep-alive" ->
+        ClientConnection /= "close", ServerConnection == "keep-alive" ->
             Socket
     end.
 
@@ -929,3 +991,83 @@ is_ipv6_host(Host) ->
                     end
             end
     end.
+
+% What about the timeout?
+%%------------------------------------------------------------------------------
+%% @private
+%% @doc If we are using a pool, it takes the socket from the pool if it exist,
+%% otherwise it creates a new socket. If we are not using pool, it just creates
+%% a new socket. If the option pool_ensure its set to true, it creates a
+%% pool dinamically.
+%% @end
+%%------------------------------------------------------------------------------
+connect_socket(#client_state{pool = undefined} = State) ->
+    connect_socket_return(new_socket(State), State);
+connect_socket(#client_state{pool = _Pool} = State) ->
+    connect_socket_return(connect_pool(State), State).
+
+connect_socket_return({ok, Socket}, State) ->
+    {ok, State#client_state{socket = Socket}};
+connect_socket_return(Error, State) ->
+    {Error, State}.
+
+-spec connect_pool(#client_state{}) -> {ok, socket()} | {error, atom()}.
+connect_pool(State = #client_state{pool_options = Options,
+                                   pool = Pool}) ->
+    {Host, Port, Ssl} = request_first_destination(State),
+    case lhttpc_manager:ensure_call(Pool, self(), Host, Port, Ssl, Options) of
+        {ok, no_socket} ->
+            %% ensure_call does not open a socket if the pool doesnt have one
+            new_socket(State);
+        Reply ->
+            Reply
+    end.
+
+%%------------------------------------------------------------------------------
+%% @private
+%% @doc Creates a new socket using the options included in the client state.
+%% end
+%%------------------------------------------------------------------------------
+new_socket(#client_state{connect_timeout = Timeout, connect_options = ConnectOptions} = State) ->
+    {Host, Port, Ssl} = request_first_destination(State),
+    ConnectOptions2 = case (not lists:member(inet, ConnectOptions)) andalso
+        (not lists:member(inet6, ConnectOptions)) andalso
+        is_ipv6_host(Host) of
+        true ->
+            [inet6 | ConnectOptions];
+        false ->
+            ConnectOptions
+    end,
+    SocketOptions = [binary, {packet, http}, {nodelay, true}, {reuseaddr, true},
+                     {active, false} | ConnectOptions2],
+    try lhttpc_sock:connect(Host, Port, SocketOptions, Timeout, Ssl) of
+        {ok, Socket} ->
+            {ok, Socket};
+        {error, etimedout} ->
+            %% TCP stack decided to give up
+            {error, connect_timeout};
+        {error, timeout} ->
+            {error, connect_timeout};
+        {error, 'record overflow'} ->
+            {error, ssl_error};
+        {error, _} = Error ->
+            Error
+    catch
+        exit:{{{badmatch, {error, {asn1, _}}}, _}, _} ->
+            {error, ssl_decode_error};
+        Type:Error ->
+            error_logger:error_msg("Socket connection error: ~p ~p, ~p",
+                                   [Type, Error, erlang:get_stacktrace()]),
+            {error, connection_error}
+    end.
+
+%%------------------------------------------------------------------------------
+%% @private
+%%------------------------------------------------------------------------------
+close_socket(_State = #client_state{socket = Socket, pool = Pool, ssl = Ssl}) ->
+    case Pool of
+        undefined ->
+            lhttpc_sock:close(Socket, Ssl);
+        _ ->
+            lhttpc_manager:close_socket(Pool, Socket)
+    end.
diff --git a/src/lhttpc_lib.erl b/src/lhttpc_lib.erl
index a83123b1..9c0352fa 100644
--- a/src/lhttpc_lib.erl
+++ b/src/lhttpc_lib.erl
@@ -1,5 +1,5 @@
 %%% ----------------------------------------------------------------------------
-%%% Copyright (c) 2009, Erlang Training and Consulting Ltd.
+%%% Copyright (c) 2009-2013, Erlang Solutions Ltd.
 %%% All rights reserved.
 %%%
 %%% Redistribution and use in source and binary forms, with or without
@@ -13,10 +13,10 @@
 %%%      names of its contributors may be used to endorse or promote products
 %%%      derived from this software without specific prior written permission.
 %%%
-%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS''
+%%% THIS SOFTWARE IS PROVIDED BY Erlang Solutions Ltd. ''AS IS''
 %%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 %%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE
+%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Solutions Ltd. BE
 %%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 %%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 %%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
@@ -27,32 +27,33 @@
 %%------------------------------------------------------------------------------
 %%% @private
 %%% @author Oscar Hellström 
+%%% @author Ramon Lastres Guerrero 
 %%% @doc
-%%% This module implements various library functions used in lhttpc.
+%%% This module implements various library functions used in lhttpc
 %%------------------------------------------------------------------------------
 -module(lhttpc_lib).
 
 -export([parse_url/1,
-         format_request/7,
+         format_request/8,
          header_value/2, header_value/3,
          normalize_method/1,
          maybe_atom_to_list/1,
          format_hdrs/1,
-         dec/1
-        ]).
+         dec/1,
+         get_cookies/1,
+         update_cookies/2,
+         to_lower/1]).
 
 -include("lhttpc_types.hrl").
 -include("lhttpc.hrl").
 
+-define(HTTP_LINE_END, "\r\n").
+
 %%==============================================================================
 %% Exported functions
 %%==============================================================================
 
 %%------------------------------------------------------------------------------
-%% @spec header_value(Header, Headers) -> undefined | term()
-%% Header = string()
-%% Headers = [{header(), term()}]
-%% Value = term()
 %% @doc
 %% Returns the value associated with the `Header' in `Headers'.
 %% `Header' must be a lowercase string, since every header is mangled to
@@ -64,11 +65,6 @@ header_value(Hdr, Hdrs) ->
     header_value(Hdr, Hdrs, undefined).
 
 %%------------------------------------------------------------------------------
-%% @spec header_value(Header, Headers, Default) -> Default | term()
-%% Header = string()
-%% Headers = [{string(), term()}]
-%% Value = term()
-%% Default = term()
 %% @doc
 %% Returns the value associated with the `Header' in `Headers'.
 %% `Header' must be a lowercase string, since every header is mangled to
@@ -86,11 +82,11 @@ header_value(Hdr, [{ThisHdr, Value}| Hdrs], Default) when is_atom(ThisHdr) ->
 header_value(Hdr, [{ThisHdr, Value}| Hdrs], Default) when is_binary(ThisHdr) ->
     header_value(Hdr, [{binary_to_list(ThisHdr), Value}| Hdrs], Default);
 header_value(Hdr, [{ThisHdr, Value}| Hdrs], Default) ->
-    case string:equal(string:to_lower(ThisHdr), Hdr) of
+    case string:equal(lhttpc_lib:to_lower(ThisHdr), Hdr) of
         true  -> case is_list(Value) of
-                     true -> string:strip(Value);
-                     false -> Value
-                 end;
+                true -> string:strip(Value);
+                false -> Value
+            end;
         false ->
             header_value(Hdr, Hdrs, Default)
     end;
@@ -98,9 +94,6 @@ header_value(_, [], Default) ->
     Default.
 
 %%------------------------------------------------------------------------------
-%% @spec (Item) -> OtherItem
-%%   Item = atom() | list()
-%%   OtherItem = list()
 %% @doc
 %% Will make any item, being an atom or a list, in to a list. If it is a
 %% list, it is simple returned.
@@ -113,8 +106,6 @@ maybe_atom_to_list(List) ->
     List.
 
 %%------------------------------------------------------------------------------
-%% @spec (URL) -> #lhttpc_url{}
-%%   URL = string()
 %% @doc
 %% @end
 %%------------------------------------------------------------------------------
@@ -125,45 +116,22 @@ parse_url(URL) ->
     {User, Passwd, HostPortPath} = split_credentials(CredsHostPortPath),
     {Host, PortPath} = split_host(HostPortPath, []),
     {Port, Path} = split_port(Scheme, PortPath, []),
-    #lhttpc_url{
-        host = string:to_lower(Host),
-        port = Port,
-        path = Path,
-        user = User,
-        password = Passwd,
-        is_ssl = (Scheme =:= https)
-    }.
-
-%%------------------------------------------------------------------------------
-%% @spec (Path, Method, Headers, Host, Port, Body, PartialUpload) -> Request
-%% Path = iolist()
-%% Method = atom() | string()
-%% Headers = [{atom() | string(), string()}]
-%% Host = string()
-%% Port = integer()
-%% Body = iolist()
-%% PartialUpload = true | false
+    #lhttpc_url{host = lhttpc_lib:to_lower(Host), port = Port, path = Path,
+                user = User, password = Passwd, is_ssl = (Scheme =:= https)}.
+
+%%------------------------------------------------------------------------------
 %% @doc
 %% @end
 %%------------------------------------------------------------------------------
--spec format_request(iolist(), method(), headers(), string(),
-    integer(), iolist(), boolean()) -> {boolean(), iolist()}.
-format_request(Path, Method, Hdrs, Host, Port, Body, PartialUpload) ->
-    AllHdrs = add_mandatory_hdrs(Method, Hdrs, Host, Port, Body, PartialUpload),
+-spec format_request(iolist(), method(), headers(), string(), integer(), iolist(),
+                     boolean(), {boolean(), [#lhttpc_cookie{}]}) -> {boolean(), iolist()}.
+format_request(Path, Method, Hdrs, Host, Port, Body, PartialUpload, Cookies) ->
+    AllHdrs = add_mandatory_hdrs(Path, Method, Hdrs, Host, Port, Body, PartialUpload, Cookies),
     IsChunked = is_chunked(AllHdrs),
-    {
-        IsChunked,
-        [
-            Method, " ", Path, " HTTP/1.1\r\n",
-            format_hdrs(AllHdrs),
-            format_body(Body, IsChunked)
-        ]
-    }.
-
-%%------------------------------------------------------------------------------
-%% @spec normalize_method(AtomOrString) -> Method
-%%   AtomOrString = atom() | string()
-%%   Method = string()
+    {IsChunked, [Method, " ", Path, " HTTP/1.1", ?HTTP_LINE_END, format_hdrs(AllHdrs),
+     format_body(Body, IsChunked)]}.
+
+%%------------------------------------------------------------------------------
 %% @doc
 %% Turns the method in to a string suitable for inclusion in a HTTP request
 %% line.
@@ -180,8 +148,10 @@ normalize_method(Method) ->
 %% @end
 %%------------------------------------------------------------------------------
 -spec dec(timeout()) -> timeout().
-dec(Num) when is_integer(Num) -> Num - 1;
-dec(Else)                     -> Else.
+dec(Num) when is_integer(Num) ->
+    Num - 1;
+dec(Else) ->
+    Else.
 
 %%------------------------------------------------------------------------------
 %% @doc
@@ -192,15 +162,153 @@ format_hdrs(Headers) ->
     NormalizedHeaders = normalize_headers(Headers),
     format_hdrs(NormalizedHeaders, []).
 
+%%------------------------------------------------------------------------------
+%% @doc From a list of headers returned by the server, it returns a list of
+%% cookie records, one record for each set-cookie line on the headers.
+%% @end
+%%------------------------------------------------------------------------------
+-spec get_cookies(headers()) -> [#lhttpc_cookie{}].
+get_cookies(Hdrs) ->
+    Values = [Value || {"Set-Cookie", Value} <- Hdrs],
+    lists:map(fun create_cookie_record/1, Values).
+
+%%------------------------------------------------------------------------------
+%% @private
+%% @doc Updated the state of the cookies. after we receive a response.
+%% @end
+%%------------------------------------------------------------------------------
+-spec update_cookies(headers(), [#lhttpc_cookie{}]) -> [#lhttpc_cookie{}].
+update_cookies(RespHeaders, StateCookies) ->
+    ReceivedCookies = lhttpc_lib:get_cookies(RespHeaders),
+    %% substitute the cookies with the same name, add the others.
+    Substituted = lists:foldl(fun(X, Acc) ->
+                                lists:keystore(X#lhttpc_cookie.name,
+                                                #lhttpc_cookie.name, Acc, X)
+                              end, StateCookies, ReceivedCookies),
+    %% delete the cookies whose value is set to "deleted"
+    NewCookies = [ X || X <- Substituted, X#lhttpc_cookie.value /= "deleted"],
+    %% Delete the cookies that are expired (check max-age and expire fields).
+    delete_expired_cookies(NewCookies).
+
+
+%%------------------------------------------------------------------------------
+%% @doc Converts characters in a string ro lower case.
+%% @end
+%%------------------------------------------------------------------------------
+-spec to_lower(string()) -> string().
+to_lower(String) ->
+    [char_to_lower(X) || X <- String].
+
 %%==============================================================================
 %% Internal functions
 %%==============================================================================
 
 %%------------------------------------------------------------------------------
 %% @private
-%% @doc
+%%------------------------------------------------------------------------------
+-spec delete_expired_cookies([#lhttpc_cookie{}]) -> [#lhttpc_cookie{}].
+delete_expired_cookies(Cookies) ->
+    [ X || X <- Cookies,
+           X#lhttpc_cookie.max_age == undefined orelse
+           timer:now_diff(os:timestamp(), X#lhttpc_cookie.timestamp)
+           =< X#lhttpc_cookie.max_age, X#lhttpc_cookie.expires == never orelse
+           calendar:datetime_to_gregorian_seconds(calendar:universal_time())
+           =< calendar:datetime_to_gregorian_seconds(X#lhttpc_cookie.expires)].
+
+%%------------------------------------------------------------------------------
+%% @private
+%%------------------------------------------------------------------------------
+create_cookie_record(Cookie) ->
+    [NameValue | Rest] = string:tokens(Cookie, ";"),
+    Tokens = string:tokens(NameValue, "="),
+    {Atr, AtrValue} = case length(Tokens) of
+        2 ->
+            [Name | [Value]] = Tokens,
+            {Name, Value};
+        _ ->
+            [Name | _] = Tokens,
+            Length = length(Name) + 2,
+            Value = string:substr(NameValue, Length),
+            {Name, Value}
+    end,
+    CookieRec = #lhttpc_cookie{name = Atr,
+                               value = AtrValue},
+    other_cookie_elements(Rest, CookieRec).
+
+%%------------------------------------------------------------------------------
+%% @doc Extracts the interesting fields from the cookie in the header. We ignore
+%% the domain since the client only connects to one domain at the same time.
+%% @end
+%% @private
+%%------------------------------------------------------------------------------
+other_cookie_elements([], Cookie) ->
+    Cookie;
+% sometimes seems that the E is a capital letter...
+other_cookie_elements([" Expires" ++ Value | Rest], Cookie) ->
+    "=" ++ FinalValue = Value,
+    Expires = expires_to_datetime(FinalValue),
+    other_cookie_elements(Rest, Cookie#lhttpc_cookie{expires = Expires});
+% ...sometimes it is not.
+other_cookie_elements([" expires" ++ Value | Rest], Cookie) ->
+    "=" ++ FinalValue = Value,
+    Expires = expires_to_datetime(FinalValue),
+    other_cookie_elements(Rest, Cookie#lhttpc_cookie{expires = Expires});
+other_cookie_elements([" Path" ++ Value | Rest], Cookie) ->
+    "=" ++ FinalValue = Value,
+    other_cookie_elements(Rest, Cookie#lhttpc_cookie{path = FinalValue});
+other_cookie_elements([" path" ++ Value | Rest], Cookie) ->
+    "=" ++ FinalValue = Value,
+    other_cookie_elements(Rest, Cookie#lhttpc_cookie{path = FinalValue});
+other_cookie_elements([" Max-Age" ++ Value | Rest], Cookie) ->
+    "=" ++ FinalValue = Value,
+    {Integer, _Rest} = string:to_integer(FinalValue),
+    MaxAge = Integer * 1000000, %we need it in microseconds
+    other_cookie_elements(Rest, Cookie#lhttpc_cookie{max_age = MaxAge,
+                                                     timestamp = os:timestamp()});
+other_cookie_elements([" max-age" ++ Value | Rest], Cookie) ->
+    "=" ++ FinalValue = Value,
+    {Integer, _Rest} = string:to_integer(FinalValue),
+    MaxAge = Integer * 1000000, %we need it in microseconds
+    other_cookie_elements(Rest, Cookie#lhttpc_cookie{max_age = MaxAge,
+                                                     timestamp = os:timestamp()});
+% for the moment we ignore the other attributes.
+other_cookie_elements([_Element | Rest], Cookie) ->
+    other_cookie_elements(Rest, Cookie).
+
+%%------------------------------------------------------------------------------
+%% @private
+%% @doc Parses the string contained in the expires field of a cookie and returns
+%% the date in datetime() format defined in calendar module.
 %% @end
 %%------------------------------------------------------------------------------
+-spec expires_to_datetime(string()) ->
+    {{integer(), integer(), integer()},{integer(),integer(),integer()}}.
+expires_to_datetime(ExpireDate) ->
+    [_Expires, Day, Month, Year, Hour, Min, Sec, _GMT] = string:tokens(ExpireDate, ", -:"),
+    {{list_to_integer(Year), month_to_integer(Month), list_to_integer(Day)},
+     {list_to_integer(Hour), list_to_integer(Min), list_to_integer(Sec)}}.
+
+
+%%------------------------------------------------------------------------------
+%% @private
+%%------------------------------------------------------------------------------
+-spec month_to_integer(string()) -> integer().
+month_to_integer("Jan") -> 1;
+month_to_integer("Feb") -> 2;
+month_to_integer("Mar") -> 3;
+month_to_integer("Apr") -> 4;
+month_to_integer("May") -> 5;
+month_to_integer("Jun") -> 6;
+month_to_integer("Jul") -> 7;
+month_to_integer("Aug") -> 8;
+month_to_integer("Sep") -> 9;
+month_to_integer("Oct") -> 10;
+month_to_integer("Nov") -> 11;
+month_to_integer("Dec") -> 12.
+
+%%------------------------------------------------------------------------------
+%% @private
+%%------------------------------------------------------------------------------
 split_scheme("http://" ++ HostPortPath) ->
     {http, HostPortPath};
 split_scheme("https://" ++ HostPortPath) ->
@@ -208,8 +316,6 @@ split_scheme("https://" ++ HostPortPath) ->
 
 %%------------------------------------------------------------------------------
 %% @private
-%% @doc
-%% @end
 %%------------------------------------------------------------------------------
 split_credentials(CredsHostPortPath) ->
     case string:tokens(CredsHostPortPath, "@") of
@@ -304,15 +410,14 @@ normalize_headers(Headers) ->
 %%------------------------------------------------------------------------------
 -spec normalize_headers(raw_headers(), headers()) -> headers().
 normalize_headers([{Header, Value} | T], Acc) when is_list(Header) ->
-    NormalizedHeader = try list_to_existing_atom(Header)
-                      catch
-                           error:badarg -> Header
-                       end,
-    NewAcc = [{NormalizedHeader, Value} | Acc],
-    normalize_headers(T, NewAcc);
+    NormalizedHeader = try
+        list_to_existing_atom(Header)
+    catch
+        error:badarg -> Header
+    end,
+    normalize_headers(T, [{NormalizedHeader, Value} | Acc]);
 normalize_headers([{Header, Value} | T], Acc) ->
-    NewAcc = [{Header, Value} | Acc],
-    normalize_headers(T, NewAcc);
+    normalize_headers(T, [{Header, Value} | Acc]);
 normalize_headers([], Acc) ->
     Acc.
 
@@ -321,12 +426,13 @@ normalize_headers([], Acc) ->
 %% @doc
 %% @end
 %%------------------------------------------------------------------------------
-format_hdrs([{Hdr, Value} | T], Acc) ->
-    NewAcc =
-        [maybe_atom_to_list(Hdr), ": ", maybe_atom_to_list(Value), "\r\n" | Acc],
+format_hdrs([{Header, Value} | T], Acc) ->
+    Header2 = maybe_atom_to_list(Header),
+    Value2 = maybe_atom_to_list(Value),
+    NewAcc = [Header2, ": ", Value2, ?HTTP_LINE_END | Acc],
     format_hdrs(T, NewAcc);
 format_hdrs([], Acc) ->
-    [Acc, "\r\n"].
+    [Acc, ?HTTP_LINE_END].
 
 %%------------------------------------------------------------------------------
 %% @private
@@ -341,10 +447,8 @@ format_body(Body, true) ->
         0 ->
             <<>>;
         Size ->
-            [
-                erlang:integer_to_list(Size, 16), <<"\r\n">>,
-                Body, <<"\r\n">>
-            ]
+            [erlang:integer_to_list(Size, 16), <>,
+             Body, <>]
     end.
 
 %%------------------------------------------------------------------------------
@@ -352,16 +456,59 @@ format_body(Body, true) ->
 %% @doc
 %% @end
 %%------------------------------------------------------------------------------
--spec add_mandatory_hdrs(method(), headers(), host(), port_num(),
-                         iolist(), boolean()) -> headers().
-add_mandatory_hdrs(Method, Hdrs, Host, Port, Body, PartialUpload) ->
+-spec add_mandatory_hdrs(string(), method(), headers(), host(), port_num(),
+                         iolist(), boolean(), {boolean(), [#lhttpc_cookie{}]}) -> headers().
+add_mandatory_hdrs(Path, Method, Hdrs, Host, Port, Body, PartialUpload, {UseCookies, Cookies}) ->
     ContentHdrs = add_content_headers(Method, Hdrs, Body, PartialUpload),
-    add_host(ContentHdrs, Host, Port).
+    case UseCookies of
+        true ->
+            % only include cookies if the cookie path is a prefix of the request path
+            % see RFC http://www.ietf.org/rfc/rfc2109.txt section 4.3.4
+            IncludeCookies = lists:filter(
+                                fun(#lhttpc_cookie{path = undefined}) ->
+                                       true;
+                                   (X) ->
+                                       IsPrefix = string:str(Path, X#lhttpc_cookie.path),
+                                       if (IsPrefix =/= 1) ->
+                                           false;
+                                       true ->
+                                           true
+                                      end
+                               end, Cookies),
+            FinalHdrs = add_cookie_headers(ContentHdrs, IncludeCookies);
+        _ ->
+            FinalHdrs = ContentHdrs
+    end,
+    add_host(FinalHdrs, Host, Port).
+
+%%------------------------------------------------------------------------------
+%% @private
+%%------------------------------------------------------------------------------
+add_cookie_headers(Hdrs, []) ->
+    Hdrs;
+add_cookie_headers(Hdrs, Cookies) ->
+    CookieString = make_cookie_string(Cookies, []),
+    [{"Cookie", CookieString} | Hdrs].
+
+%%------------------------------------------------------------------------------
+%% @private
+%%------------------------------------------------------------------------------
+make_cookie_string([], Acc) ->
+    Acc;
+make_cookie_string([Cookie | []], Acc) ->
+    Last = cookie_string(Cookie) -- "; ",
+    make_cookie_string([], Acc ++ Last);
+make_cookie_string([Cookie | Rest], Acc) ->
+    make_cookie_string(Rest,  Acc ++ cookie_string(Cookie)).
+
+%%------------------------------------------------------------------------------
+%% @private
+%%------------------------------------------------------------------------------
+cookie_string(#lhttpc_cookie{name = Name, value = Value}) ->
+    Name ++ "=" ++ Value ++ "; ".
 
 %%------------------------------------------------------------------------------
 %% @private
-%% @doc
-%% @end
 %%------------------------------------------------------------------------------
 -spec add_content_headers(string(), headers(), iolist(), boolean()) -> headers().
 add_content_headers("POST", Hdrs, Body, PartialUpload) ->
@@ -375,8 +522,6 @@ add_content_headers(_, Hdrs, _, _PartialUpload) ->
 
 %%------------------------------------------------------------------------------
 %% @private
-%% @doc
-%% @end
 %%------------------------------------------------------------------------------
 -spec add_content_headers(headers(), iolist(), boolean()) -> headers().
 add_content_headers(Hdrs, Body, false) ->
@@ -389,13 +534,13 @@ add_content_headers(Hdrs, Body, false) ->
     end;
 add_content_headers(Hdrs, _Body, true) ->
     case {header_value("content-length", Hdrs),
-         header_value("transfer-encoding", Hdrs)} of
+          header_value("transfer-encoding", Hdrs)} of
         {undefined, undefined} ->
             [{"Transfer-Encoding", "chunked"} | Hdrs];
         {undefined, TransferEncoding} ->
-            case string:to_lower(TransferEncoding) of
-            "chunked" -> Hdrs;
-            _ -> erlang:error({error, unsupported_transfer_encoding})
+            case lhttpc_lib:to_lower(TransferEncoding) of
+                "chunked" -> Hdrs;
+                _ -> erlang:error({error, unsupported_transfer_encoding})
             end;
         {_Length, undefined} ->
             Hdrs;
@@ -424,8 +569,8 @@ add_host(Hdrs, Host, Port) ->
 %%------------------------------------------------------------------------------
 -spec is_chunked(headers()) -> boolean().
 is_chunked(Hdrs) ->
-    TransferEncoding = string:to_lower(
-        header_value("transfer-encoding", Hdrs, "undefined")),
+    TransferEncoding = lhttpc_lib:to_lower(
+            header_value("transfer-encoding", Hdrs, "undefined")),
     case TransferEncoding of
         "chunked" -> true;
         _ -> false
@@ -457,3 +602,36 @@ maybe_ipv6_enclose(Host) ->
         _ ->
             Host
     end.
+
+%%------------------------------------------------------------------------------
+%% @private
+%% @doc
+%% @end
+%%------------------------------------------------------------------------------
+char_to_lower($A) -> $a;
+char_to_lower($B) -> $b;
+char_to_lower($C) -> $c;
+char_to_lower($D) -> $d;
+char_to_lower($E) -> $e;
+char_to_lower($F) -> $f;
+char_to_lower($G) -> $g;
+char_to_lower($H) -> $h;
+char_to_lower($I) -> $i;
+char_to_lower($J) -> $j;
+char_to_lower($K) -> $k;
+char_to_lower($L) -> $l;
+char_to_lower($M) -> $m;
+char_to_lower($N) -> $n;
+char_to_lower($O) -> $o;
+char_to_lower($P) -> $p;
+char_to_lower($Q) -> $q;
+char_to_lower($R) -> $r;
+char_to_lower($S) -> $s;
+char_to_lower($T) -> $t;
+char_to_lower($U) -> $u;
+char_to_lower($V) -> $v;
+char_to_lower($W) -> $w;
+char_to_lower($X) -> $x;
+char_to_lower($Y) -> $y;
+char_to_lower($Z) -> $z;
+char_to_lower(Ch) -> Ch.
diff --git a/src/lhttpc_manager.erl b/src/lhttpc_manager.erl
index 80ee047c..7504e671 100644
--- a/src/lhttpc_manager.erl
+++ b/src/lhttpc_manager.erl
@@ -1,5 +1,5 @@
 %%% ----------------------------------------------------------------------------
-%%% Copyright (c) 2009, Erlang Training and Consulting Ltd.
+%%% Copyright (c) 2009-2013, Erlang Solutions Ltd.
 %%% All rights reserved.
 %%%
 %%% Redistribution and use in source and binary forms, with or without
@@ -9,14 +9,14 @@
 %%%    * Redistributions in binary form must reproduce the above copyright
 %%%      notice, this list of conditions and the following disclaimer in the
 %%%      documentation and/or other materials provided with the distribution.
-%%%    * Neither the name of Erlang Training and Consulting Ltd. nor the
+%%%    * Neither the name of Erlang Solutions Ltd. nor the
 %%%      names of its contributors may be used to endorse or promote products
 %%%      derived from this software without specific prior written permission.
 %%%
-%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS''
+%%% THIS SOFTWARE IS PROVIDED BY Erlang Solutions Ltd. ''AS IS''
 %%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 %%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE
+%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Solutions Ltd. BE
 %%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 %%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 %%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
@@ -27,18 +27,18 @@
 %%------------------------------------------------------------------------------
 %%% @author Oscar Hellström 
 %%% @author Filipe David Manana 
+%%% @author Diana Parra Corbacho 
+%%% @author Ramon Lastres Guerrero 
 %%% @doc Connection manager for the HTTP client.
-%%% This gen_server is responsible for keeping track of persistent
-%%% connections to HTTP servers. The only interesting API is
-%%% `connection_count/0' and `connection_count/1'.
-%%% The gen_server is supposed to be started by a supervisor, which is
-%%% normally {@link lhttpc_sup}.
+%%% This module provides the pool of connections functionality. It exports funcions
+%%% intended to provide information and manage the existing pools.
 %%% @end
 %%------------------------------------------------------------------------------
 -module(lhttpc_manager).
 
 %% Exported functions
 -export([start_link/0, start_link/1,
+         new_pool/3,
          client_count/1,
          connection_count/1, connection_count/2,
          update_connection_timeout/2,
@@ -46,8 +46,8 @@
          list_pools/0,
          set_max_pool_size/2,
          ensure_call/6,
-         client_done/5
-        ]).
+         client_done/5,
+         close_socket/2]).
 
 %% Callbacks
 -export([init/1,
@@ -55,8 +55,7 @@
          handle_cast/2,
          handle_info/2,
          code_change/3,
-         terminate/2
-        ]).
+         terminate/2]).
 
 -behaviour(gen_server).
 
@@ -64,20 +63,18 @@
 -include("lhttpc.hrl").
 
 -record(httpc_man, {
-        destinations = dict:new(),
-        sockets = dict:new(),
-        clients = dict:new(), % Pid => {Dest, MonRef}
-        queues = dict:new(),  % Dest => queue of Froms
-        max_pool_size = 50 :: non_neg_integer(),
-        timeout = 300000 :: non_neg_integer()
-    }).
+            destinations = dict:new(),
+            sockets = dict:new(),
+            clients = dict:new(), % Pid => {Dest, MonRef}
+            queues = dict:new(),  % Dest => queue of Froms
+            max_pool_size = 50 :: non_neg_integer(),
+            timeout = 300000 :: non_neg_integer()}).
 
 %%==============================================================================
 %% Exported functions
 %%==============================================================================
 
 %%------------------------------------------------------------------------------
-%% @spec (PoolPidOrName) -> list()
 %% @doc Returns the current settings in state for the
 %% specified lhttpc pool (manager).
 %% @end
@@ -102,17 +99,15 @@ set_max_pool_size(PidOrName, Size) when is_integer(Size), Size > 0 ->
 list_pools() ->
     Children = supervisor:which_children(lhttpc_sup),
     lists:foldl(fun(In, Acc) ->
-                        case In of
-                            {N, P, _, [lhttpc_manager]} ->
-                                [{N, dump_settings(P)} | Acc];
-                            _ ->
-                                Acc
-                        end
-                end, [], Children).
+                case In of
+                    {N, P, _, [lhttpc_manager]} ->
+                        [{N, dump_settings(P)} | Acc];
+                    _ ->
+                        Acc
+                end
+        end, [], Children).
 
 %%------------------------------------------------------------------------------
-%% @spec (PoolPidOrName) -> Count
-%%    Count = integer()
 %% @doc Returns the total number of active clients maintained by the
 %% specified lhttpc pool (manager).
 %% @end
@@ -122,8 +117,6 @@ client_count(PidOrName) ->
     gen_server:call(PidOrName, client_count).
 
 %%------------------------------------------------------------------------------
-%% @spec (PoolPidOrName) -> Count
-%%    Count = integer()
 %% @doc Returns the total number of active connections maintained by the
 %% specified lhttpc pool (manager).
 %% @end
@@ -133,26 +126,16 @@ connection_count(PidOrName) ->
     gen_server:call(PidOrName, connection_count).
 
 %%------------------------------------------------------------------------------
-%% @spec (PoolPidOrName, Destination) -> Count
-%%    PoolPidOrName = pid() | atom()
-%%    Destination = {Host, Port, Ssl}
-%%    Host = string()
-%%    Port = integer()
-%%    Ssl = boolean()
-%%    Count = integer()
 %% @doc Returns the number of active connections to the specific
 %% `Destination' maintained by the httpc manager.
 %% @end
 %%------------------------------------------------------------------------------
--spec connection_count(pool_id(), destination()) -> non_neg_integer().
+-spec connection_count(pool_id(), Destination :: destination()) -> non_neg_integer().
 connection_count(PidOrName, {Host, Port, Ssl}) ->
     Destination = {string:to_lower(Host), Port, Ssl},
     gen_server:call(PidOrName, {connection_count, Destination}).
 
 %%------------------------------------------------------------------------------
-%% @spec (PoolPidOrName, Timeout) -> ok
-%%    PoolPidOrName = pid() | atom()
-%%    Timeout = integer()
 %% @doc Updates the timeout for persistent connections.
 %% This will only affect future sockets handed to the manager. The sockets
 %% already managed will keep their timers.
@@ -163,7 +146,15 @@ update_connection_timeout(PidOrName, Milliseconds) ->
     gen_server:cast(PidOrName, {update_timeout, Milliseconds}).
 
 %%------------------------------------------------------------------------------
-%% @spec () -> {ok, pid()}
+%% @private
+%% @doc
+%% @end
+%%------------------------------------------------------------------------------
+close_socket(PidOrName, Socket) ->
+    gen_server:cast(PidOrName, {remove_socket, Socket}).
+
+%%------------------------------------------------------------------------------
+%% @private
 %% @doc Starts and link to the gen server.
 %% This is normally called by a supervisor.
 %% @end
@@ -174,6 +165,7 @@ start_link() ->
 
 
 %%------------------------------------------------------------------------------
+%% @private
 %% @doc
 %% @end
 %%------------------------------------------------------------------------------
@@ -189,52 +181,72 @@ start_link(Options0) ->
     end.
 
 %%------------------------------------------------------------------------------
+%% @private
 %% @doc If call contains pool_ensure option, dynamically create the pool with
 %% configured parameters. Checks the pool for a socket connected to the
 %% destination and returns it if it exists, 'undefined' otherwise.
 %% @end
 %%------------------------------------------------------------------------------
--spec ensure_call(pool_id(), pid(), host(), port_num(), boolean(), options()) ->
-                        socket() | 'no_socket'.
+-spec ensure_call(pool_id(), pid(), host(), port_num(), boolean(), pool_options()) ->
+    {ok, socket()} | {ok, 'no_socket'} | {error, term()}.
 ensure_call(Pool, Pid, Host, Port, Ssl, Options) ->
     SocketRequest = {socket, Pid, Host, Port, Ssl},
-    try gen_server:call(Pool, SocketRequest, infinity) of
-        {ok, S} ->
-            %% Re-using HTTP/1.1 connections
-            S;
-        no_socket ->
-            %% Opening a new HTTP/1.1 connection
-            undefined
+    try
+        gen_server:call(Pool, SocketRequest, 100)
     catch
-        exit:{noproc, Reason} ->
+        exit:{noproc, _Reason} ->
             case proplists:get_value(pool_ensure, Options, false) of
                 true ->
                     {ok, DefaultTimeout} = application:get_env(
-                                             lhttpc,
-                                             connection_timeout),
+                            lhttpc,
+                            connection_timeout),
                     ConnTimeout = proplists:get_value(pool_connection_timeout,
                                                       Options,
                                                       DefaultTimeout),
                     {ok, DefaultMaxPool} = application:get_env(
-                                             lhttpc,
-                                             pool_size),
+                            lhttpc,
+                            pool_size),
                     PoolMaxSize = proplists:get_value(pool_max_size,
                                                       Options,
                                                       DefaultMaxPool),
-                    case lhttpc:add_pool(Pool, ConnTimeout, PoolMaxSize) of
+                    case new_pool(Pool, ConnTimeout, PoolMaxSize) of
                         {ok, _Pid} ->
                             ensure_call(Pool, Pid, Host, Port, Ssl, Options);
-                        _ ->
-                            %% Failed to create pool, exit as expected
-                            exit({noproc, Reason})
+                        Error ->
+                            Error
                     end;
                 false ->
                     %% No dynamic pool creation, exit as expected
-                    exit({noproc, Reason})
+                    {error, unknown_pool}
             end
     end.
 
 %%------------------------------------------------------------------------------
+%% @private
+%% @doc Creates a new pool.
+%% @end
+%%------------------------------------------------------------------------------
+-spec new_pool(atom(), non_neg_integer(), poolsize()) ->
+    {ok, pid()} | {error, term()}.
+new_pool(Pool, ConnTimeout, PoolSize) ->
+    ChildSpec = {Pool,
+                 {lhttpc_manager, start_link, [[{name, Pool},
+                                                {connection_timeout, ConnTimeout},
+                                                {pool_size, PoolSize}]]},
+                 permanent, 10000, worker, [lhttpc_manager]},
+    case supervisor:start_child(lhttpc_sup, ChildSpec) of
+        {error, {already_started, _Pid}} ->
+            {error, already_exists};
+        {error, Error} ->
+            {error, Error};
+        {ok, Pid} ->
+            {ok, Pid};
+        {ok, Pid, _Info} ->
+            {ok, Pid}
+    end.
+
+%%------------------------------------------------------------------------------
+%% @private
 %% @doc A client has finished one request and returns the socket to the pool,
 %% which can be new or not.
 %% @end
@@ -281,7 +293,7 @@ handle_call({socket, Pid, Host, Port, Ssl}, {Pid, _Ref} = From, State) ->
         max_pool_size = MaxSize,
         clients = Clients,
         queues = Queues
-    } = State,
+        } = State,
     Dest = {Host, Port, Ssl},
     {Reply0, State2} = find_socket(Dest, Pid, State),
     case Reply0 of
@@ -294,11 +306,12 @@ handle_call({socket, Pid, Host, Port, Ssl}, {Pid, _Ref} = From, State) ->
                     Queues2 = add_to_queue(Dest, From, Queues),
                     {noreply, State2#httpc_man{queues = Queues2}};
                 false ->
-                    {reply, no_socket, monitor_client(Dest, From, State2)}
+                    {reply, {ok, no_socket}, monitor_client(Dest, From, State2)}
             end
     end;
 handle_call(dump_settings, _, State) ->
-    {reply, [{max_pool_size, State#httpc_man.max_pool_size}, {timeout, State#httpc_man.timeout}], State};
+    {reply, [{max_pool_size, State#httpc_man.max_pool_size},
+             {timeout, State#httpc_man.timeout}], State};
 handle_call(client_count, _, State) ->
     {reply, dict:size(State#httpc_man.clients), State};
 handle_call(connection_count, _, State) ->
@@ -328,6 +341,8 @@ handle_cast({update_timeout, Milliseconds}, State) ->
     {noreply, State#httpc_man{timeout = Milliseconds}};
 handle_cast({set_max_pool_size, Size}, State) ->
     {noreply, State#httpc_man{max_pool_size = Size}};
+handle_cast({remove_socket, Socket}, State) ->
+    {noreply, remove_socket(Socket, State)};
 handle_cast(_, State) ->
     {noreply, State}.
 
@@ -356,7 +371,7 @@ handle_info({'DOWN', MonRef, process, Pid, _Reason}, State) ->
         empty ->
             {noreply, State#httpc_man{clients = Clients2}};
         {ok, From, Queues2} ->
-            gen_server:reply(From, no_socket),
+            gen_server:reply(From, {ok, no_socket}),
             State2 = State#httpc_man{queues = Queues2, clients = Clients2},
             {noreply, monitor_client(Dest, From, State2)}
     end;
@@ -394,9 +409,9 @@ find_socket({_, _, Ssl} = Dest, Pid, State) ->
                     {_, Timer} = dict:fetch(Socket, State#httpc_man.sockets),
                     cancel_timer(Timer, Socket),
                     NewState = State#httpc_man{
-                        destinations = update_dest(Dest, Sockets, Dests),
-                        sockets = dict:erase(Socket, State#httpc_man.sockets)
-                    },
+                            destinations = update_dest(Dest, Sockets, Dests),
+                            sockets = dict:erase(Socket, State#httpc_man.sockets)
+                            },
                     {{ok, Socket}, NewState};
                 {error, badarg} -> % Pid has timed out, reuse for someone else
                     lhttpc_sock:setopts(Socket, [{active, once}], Ssl),
@@ -421,7 +436,7 @@ remove_socket(Socket, State) ->
             State#httpc_man{
                 destinations = update_dest(Dest, Sockets, Dests),
                 sockets = dict:erase(Socket, State#httpc_man.sockets)
-            };
+                };
         error ->
             State
     end.
@@ -442,7 +457,7 @@ store_socket({_, _, Ssl} = Dest, Socket, State) ->
     State#httpc_man{
         destinations = dict:store(Dest, Sockets, Dests),
         sockets = dict:store(Socket, {Dest, Timer}, State#httpc_man.sockets)
-    }.
+        }.
 
 %------------------------------------------------------------------------------
 %% @private
@@ -507,8 +522,8 @@ queue_out({_Host, _Port, _Ssl} = Dest, Queues) ->
 %------------------------------------------------------------------------------
 %% @private
 %%------------------------------------------------------------------------------
-deliver_socket(Socket, {_, _, Ssl} = Dest, State) ->
-    case queue_out(Dest, State#httpc_man.queues) of
+deliver_socket(Socket, {_, _, Ssl} = Dest, #httpc_man{queues = Queues} = State) ->
+    case queue_out(Dest, Queues) of
         empty ->
             store_socket(Dest, Socket, State);
         {ok, {PidWaiter, _} = FromWaiter, Queues2} ->
@@ -529,9 +544,9 @@ deliver_socket(Socket, {_, _, Ssl} = Dest, State) ->
 %------------------------------------------------------------------------------
 %% @private
 %%------------------------------------------------------------------------------
-monitor_client(Dest, {Pid, _} = _From, State) ->
+monitor_client(Dest, {Pid, _} = _From, #httpc_man{clients = Clients} = State) ->
     MonRef = erlang:monitor(process, Pid),
-    Clients2 = dict:store(Pid, {Dest, MonRef}, State#httpc_man.clients),
+    Clients2 = dict:store(Pid, {Dest, MonRef}, Clients),
     State#httpc_man{clients = Clients2}.
 
 %------------------------------------------------------------------------------
diff --git a/src/lhttpc_sock.erl b/src/lhttpc_sock.erl
index 306779d5..c8b9e646 100644
--- a/src/lhttpc_sock.erl
+++ b/src/lhttpc_sock.erl
@@ -1,5 +1,5 @@
 %%% ----------------------------------------------------------------------------
-%%% Copyright (c) 2009, Erlang Training and Consulting Ltd.
+%%% Copyright (c) 2009-2013, Erlang Solutions Ltd.
 %%% All rights reserved.
 %%%
 %%% Redistribution and use in source and binary forms, with or without
@@ -9,14 +9,14 @@
 %%%    * Redistributions in binary form must reproduce the above copyright
 %%%      notice, this list of conditions and the following disclaimer in the
 %%%      documentation and/or other materials provided with the distribution.
-%%%    * Neither the name of Erlang Training and Consulting Ltd. nor the
+%%%    * Neither the name of Erlang Solutions Ltd. nor the
 %%%      names of its contributors may be used to endorse or promote products
 %%%      derived from this software without specific prior written permission.
 %%%
-%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS''
+%%% THIS SOFTWARE IS PROVIDED BY Erlang Solutions Ltd. ''AS IS''
 %%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 %%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE
+%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Solutions Ltd. BE
 %%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 %%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 %%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
@@ -38,8 +38,7 @@
          send/3,
          controlling_process/3,
          setopts/3,
-         close/2
-        ]).
+         close/2]).
 
 -include("lhttpc_types.hrl").
 
@@ -48,14 +47,6 @@
 %%==============================================================================
 
 %%------------------------------------------------------------------------------
-%% @spec (Host, Port, Options, Timeout, SslFlag) -> {ok, Socket} | {error, Reason}
-%%   Host = string() | ip_address()
-%%   Port = integer()
-%%   Options = [{atom(), term()} | atom()]
-%%   Timeout = infinity | integer()
-%%   SslFlag = boolean()
-%%   Socket = socket()
-%%   Reason = atom()
 %% @doc
 %% Connects to `Host' and `Port'.
 %% Will use the `ssl' module if `SslFlag' is `true' and gen_tcp otherwise.
@@ -70,12 +61,6 @@ connect(Host, Port, Options, Timeout, false) ->
     gen_tcp:connect(Host, Port, Options, Timeout).
 
 %%------------------------------------------------------------------------------
-%% @spec (Socket, SslFlag) -> {ok, Data} | {error, Reason}
-%%   Socket = socket()
-%%   Length = integer()
-%%   SslFlag = boolean()
-%%   Data = term()
-%%   Reason = atom()
 %% @doc
 %% Reads available bytes from `Socket'.
 %% Will block untill data is available on the socket and return the first
@@ -90,12 +75,6 @@ recv(Socket, false) ->
     gen_tcp:recv(Socket, 0).
 
 %%------------------------------------------------------------------------------
-%% @spec (Socket, Length, SslFlag) -> {ok, Data} | {error, Reason}
-%%   Socket = socket()
-%%   Length = integer()
-%%   SslFlag = boolean()
-%%   Data = term()
-%%   Reason = atom()
 %% @doc
 %% Receives `Length' bytes from `Socket'.
 %% Will block untill `Length' bytes is available.
@@ -110,11 +89,6 @@ recv(Socket, Length, false) ->
     gen_tcp:recv(Socket, Length).
 
 %%------------------------------------------------------------------------------
-%% @spec (Socket, Data, SslFlag) -> ok | {error, Reason}
-%%   Socket = socket()
-%%   Data = iolist()
-%%   SslFlag = boolean()
-%%   Reason = atom()
 %% @doc
 %% Sends data on a socket.
 %% Will use the `ssl' module if `SslFlag' is set to `true', otherwise the
@@ -137,8 +111,7 @@ send(Socket, Request, false) ->
 %% Sets the controlling proces for the `Socket'.
 %% @end
 %%------------------------------------------------------------------------------
--spec controlling_process(socket(), pid() | atom(), boolean()) ->
-    ok | {error, atom()}.
+-spec controlling_process(socket(), pid() | atom(), boolean()) -> ok | {error, atom()}.
 controlling_process(Socket, Controller, IsSsl) when is_atom(Controller) ->
     controlling_process(Socket, whereis(Controller), IsSsl);
 controlling_process(Socket, Pid, true) ->
@@ -147,11 +120,6 @@ controlling_process(Socket, Pid, false) ->
     gen_tcp:controlling_process(Socket, Pid).
 
 %%------------------------------------------------------------------------------
-%% @spec (Socket, Options, SslFlag) -> ok | {error, Reason}
-%%   Socket = socket()
-%%   Options = [atom() | {atom(), term()}]
-%%   SslFlag = boolean()
-%%   Reason = atom()
 %% @doc
 %% Sets options for a socket. Look in `inet:setopts/2' for more info.
 %% @end
@@ -163,10 +131,6 @@ setopts(Socket, Options, false) ->
     inet:setopts(Socket, Options).
 
 %%------------------------------------------------------------------------------
-%% @spec (Socket, SslFlag) -> ok | {error, Reason}
-%%   Socket = socket()
-%%   SslFlag = boolean()
-%%   Reason = atom()
 %% @doc
 %% Closes a socket.
 %% @end
diff --git a/src/lhttpc_sup.erl b/src/lhttpc_sup.erl
index ed917f9d..f5c59d7a 100644
--- a/src/lhttpc_sup.erl
+++ b/src/lhttpc_sup.erl
@@ -1,5 +1,5 @@
 %%% ----------------------------------------------------------------------------
-%%% Copyright (c) 2009, Erlang Training and Consulting Ltd.
+%%% Copyright (c) 2009-2013, Erlang Solutions Ltd.
 %%% All rights reserved.
 %%%
 %%% Redistribution and use in source and binary forms, with or without
@@ -9,14 +9,14 @@
 %%%    * Redistributions in binary form must reproduce the above copyright
 %%%      notice, this list of conditions and the following disclaimer in the
 %%%      documentation and/or other materials provided with the distribution.
-%%%    * Neither the name of Erlang Training and Consulting Ltd. nor the
+%%%    * Neither the name of Erlang Solutions Ltd. nor the
 %%%      names of its contributors may be used to endorse or promote products
 %%%      derived from this software without specific prior written permission.
 %%%
-%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS''
+%%% THIS SOFTWARE IS PROVIDED BY Erlang Solutions Ltd. ''AS IS''
 %%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 %%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE
+%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Solutions Ltd. BE
 %%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 %%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 %%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
@@ -25,6 +25,7 @@
 %%% ----------------------------------------------------------------------------
 
 %%------------------------------------------------------------------------------
+%%% @private
 %%% @author Oscar Hellström 
 %%% @doc Top supervisor for the lhttpc application.
 %%% This is normally started by the application behaviour implemented in
@@ -42,8 +43,7 @@
 -export([init/1]).
 
 %%------------------------------------------------------------------------------
-%% @spec () -> {ok, pid()} | {error, Reason}
-%% Reason = atom()
+%% @private
 %% @doc Starts and links to the supervisor.
 %% This is normally called from an application behaviour or from another
 %% supervisor.
@@ -61,7 +61,4 @@ start_link() ->
 %% @hidden
 %%------------------------------------------------------------------------------
 init(_) ->
-    LHTTPCManager = {lhttpc_manager, {lhttpc_manager, start_link,
-                     [[{name, lhttpc_manager}]]},
-                     permanent, 10000, worker, [lhttpc_manager]},
-    {ok, {{one_for_one, 10, 1}, [LHTTPCManager]}}.
+    {ok, {{one_for_one, 10, 1}, []}}.
diff --git a/test/lhttpc_client_tests.erl b/test/lhttpc_client_tests.erl
new file mode 100644
index 00000000..43a6d5bf
--- /dev/null
+++ b/test/lhttpc_client_tests.erl
@@ -0,0 +1,43 @@
+%%%=============================================================================
+%%% @copyright (C) 1999-2013, Erlang Solutions Ltd
+%%% @author Diana Corbacho 
+%%% @doc Unit tests for lhttpc_client
+%%% @end
+%%%=============================================================================
+-module(lhttpc_client_tests).
+-copyright("2012, Erlang Solutions Ltd.").
+
+-include_lib("eunit/include/eunit.hrl").
+
+-compile(export_all).
+
+fail_connect_test() ->
+    ?assertEqual({error, econnrefused},
+         lhttpc_client:start({{"localhost", 8080, false}, []}, [])).
+
+fail_connect_pool_test_() ->
+    {foreach,
+     fun() ->
+         ok = application:start(ssl),
+         ok = application:start(lhttpc)
+     end,
+     fun(_) ->
+         application:stop(lhttpc),
+         application:stop(ssl)
+     end,
+     [{"Fail to connect on ensure pool",
+       fun() ->
+           ?assertMatch({error, econnrefused},
+                lhttpc_client:start({{"localhost", 8080, false},
+                         [{pool_options,
+                           [{pool, my_test_pool},
+                            {pool_ensure, true}]}]}, []))
+       end},
+      {"Fail to connect - no pool",
+       fun() ->
+           ?assertEqual({error, unknown_pool},
+                lhttpc_client:start({{"localhost", 8080, false},
+                         [{pool_options,
+                           [{pool, my_test_pool}]}]}, []))
+       end}]
+    }.
diff --git a/test/lhttpc_lib_tests.erl b/test/lhttpc_lib_tests.erl
index c8e135a3..24132efd 100644
--- a/test/lhttpc_lib_tests.erl
+++ b/test/lhttpc_lib_tests.erl
@@ -1,7 +1,7 @@
 %%% ----------------------------------------------------------------------------
-%%% Copyright (c) 2009, Erlang Training and Consulting Ltd.
+%%% Copyright (c) 2009-2013, Erlang Training and Consulting Ltd.
 %%% All rights reserved.
-%%% 
+%%%
 %%% Redistribution and use in source and binary forms, with or without
 %%% modification, are permitted provided that the following conditions are met:
 %%%    * Redistributions of source code must retain the above copyright
@@ -9,14 +9,14 @@
 %%%    * Redistributions in binary form must reproduce the above copyright
 %%%      notice, this list of conditions and the following disclaimer in the
 %%%      documentation and/or other materials provided with the distribution.
-%%%    * Neither the name of Erlang Training and Consulting Ltd. nor the
+%%%    * Neither the name of Erlang Solutions Ltd. nor the
 %%%      names of its contributors may be used to endorse or promote products
 %%%      derived from this software without specific prior written permission.
-%%% 
-%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS''
+%%%
+%%% THIS SOFTWARE IS PROVIDED BY Erlang Solutions Ltd. ''AS IS''
 %%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 %%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE
+%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Solutions Ltd. BE
 %%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 %%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 %%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
@@ -31,6 +31,21 @@
 -include("../include/lhttpc.hrl").
 -include_lib("eunit/include/eunit.hrl").
 
+-define(HEADER1, [{"X-Frame-Options","SAMEORIGIN"},
+          {"X-Xss-Protection","1; mode=block"},
+          {"Content-Length","221"},
+          {"Server","gws"},
+          {"Date","Tue, 29 Jan 2013 10:31:52 GMT"},
+          {"P3p",
+           "CP=\"This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info.\""},
+          {"Set-Cookie",
+           "NID=67=gWDe_1hs0LbFdFRIiHXh8qQT_oh_2T2e2tPU3su6azclQH0FGbIpkHYkZJ1kIENFScdIWsnaHd3fUL-J8dZ8YApccTTmpfAgxgCStTspaZrCBRLG0SHRiAZz-Lkj8tyk; expires=Wed, 31-Jul-2013 10:31:52 GMT; path=/; domain=.google.com; HttpOnly"},
+          {"Set-Cookie",
+           "PREF=ID=d8f03b98b080a98a:FF=0:TM=1359455512:LM=1359455512:S=x-lfwE8swDlcyxXl; expires=Thu, 29-Jan-2015 10:31:52 GMT; path=/; domain=.google.com"},
+          {"Content-Type","text/html; charset=UTF-8"},
+          {"Cache-Control","private"},
+          {"Location","http://www.google.co.uk/"}]).
+
 parse_url_test_() ->
     [
         ?_assertEqual(#lhttpc_url{
@@ -224,3 +239,24 @@ parse_url_test_() ->
                         },
                       lhttpc_lib:parse_url("http://www.example.com?a=b"))
     ].
+
+get_cookies_test_() ->
+    [
+        ?_assertEqual([#lhttpc_cookie{
+              name = "NID",
+              value = "67=gWDe_1hs0LbFdFRIiHXh8qQT_oh_2T2e2tPU3su6azclQH0FGbIpkHYkZJ1kIENFScdIWsnaHd3fUL-J8dZ8YApccTTmpfAgxgCStTspaZrCBRLG0SHRiAZz-Lkj8tyk",
+              expires = {{2013, 7, 31}, {10, 31, 52}},
+              path = "/",
+              max_age = undefined,
+              timestamp = undefined
+             },
+               #lhttpc_cookie{
+                       name = "PREF",
+                       value = "ID=d8f03b98b080a98a:FF=0:TM=1359455512:LM=1359455512:S=x-lfwE8swDlcyxXl",
+                       expires = {{2015, 1, 29}, {10, 31, 52}},
+                       path = "/",
+                       max_age = undefined,
+                       timestamp = undefined
+                      }],
+                      lhttpc_lib:get_cookies(?HEADER1))
+    ].
diff --git a/test/lhttpc_manager_tests.erl b/test/lhttpc_manager_tests.erl
index 4e24afe0..3630292e 100644
--- a/test/lhttpc_manager_tests.erl
+++ b/test/lhttpc_manager_tests.erl
@@ -1,7 +1,7 @@
 %%% ----------------------------------------------------------------------------
-%%% Copyright (c) 2009, Erlang Training and Consulting Ltd.
+%%% Copyright (c) 2009-2013, Erlang Solutions Ltd.
 %%% All rights reserved.
-%%% 
+%%%
 %%% Redistribution and use in source and binary forms, with or without
 %%% modification, are permitted provided that the following conditions are met:
 %%%    * Redistributions of source code must retain the above copyright
@@ -9,14 +9,14 @@
 %%%    * Redistributions in binary form must reproduce the above copyright
 %%%      notice, this list of conditions and the following disclaimer in the
 %%%      documentation and/or other materials provided with the distribution.
-%%%    * Neither the name of Erlang Training and Consulting Ltd. nor the
+%%%    * Neither the name of Erlang Solutions Ltd. nor the
 %%%      names of its contributors may be used to endorse or promote products
 %%%      derived from this software without specific prior written permission.
-%%% 
-%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS''
+%%%
+%%% THIS SOFTWARE IS PROVIDED BY Erlang Solutions Ltd. ''AS IS''
 %%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 %%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE
+%%% ARE DISCLAIMED. IN NO EVENT SHALL Solutions Ltd. BE
 %%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 %%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 %%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
@@ -61,13 +61,14 @@ manager_test_() ->
 
 empty_manager() ->
     LS = socket_server:listen(),
+    lhttpc:add_pool(lhttpc_manager),
     link(whereis(lhttpc_manager)), % want to make sure it doesn't crash
     ?assertEqual(0, lhttpc_manager:connection_count(lhttpc_manager)),
 
     Client = spawn_client(),
     ?assertEqual(ok, ping_client(Client)),
 
-    ?assertEqual(no_socket, client_peek_socket(Client)),
+    ?assertEqual({ok, no_socket}, client_peek_socket(Client)),
     ?assertEqual(0, lhttpc_manager:connection_count(lhttpc_manager)),
 
     ?assertEqual(ok, stop_client(Client)),
@@ -79,6 +80,7 @@ empty_manager() ->
 
 one_socket() ->
     LS = socket_server:listen(),
+    lhttpc:add_pool(lhttpc_manager),
     link(whereis(lhttpc_manager)), % want to make sure it doesn't crash
     ?assertEqual(0, lhttpc_manager:connection_count(lhttpc_manager)),
 
@@ -111,6 +113,7 @@ one_socket() ->
 
 connection_timeout() ->
     LS = socket_server:listen(),
+    lhttpc:add_pool(lhttpc_manager),
     link(whereis(lhttpc_manager)), % want to make sure it doesn't crash
     ok = lhttpc_manager:update_connection_timeout(lhttpc_manager, 3000),
     erlang:yield(), % make sure lhttpc_manager processes the message
@@ -149,6 +152,7 @@ connection_timeout() ->
     ok.
 
 many_sockets() ->
+    lhttpc:add_pool(lhttpc_manager),
     link(whereis(lhttpc_manager)), % want to make sure it doesn't crash
     LS = socket_server:listen(),
     Client1 = spawn_client(),
@@ -279,6 +283,7 @@ many_sockets() ->
 
 closed_race_cond() ->
     LS = socket_server:listen(),
+    lhttpc:add_pool(lhttpc_manager),
     link(whereis(lhttpc_manager)), % want to make sure it doesn't crash
     ?assertEqual(0, lhttpc_manager:connection_count(lhttpc_manager)),
 
@@ -312,7 +317,7 @@ closed_race_cond() ->
         after 5000 -> erlang:error("Timeout receiving result from child process")
     end,
 
-    ?assertMatch(no_socket, Result2),
+    ?assertMatch({ok, no_socket}, Result2),
     ?assertEqual(0, lhttpc_manager:connection_count(lhttpc_manager)),
 
     ?assertEqual(ok, stop_client(Client)),
@@ -345,7 +350,7 @@ client_loop(Parent, Socket) ->
         {connect, Ref} ->
             Args = {socket, self(), ?HOST, get_port(), ?SSL},
             NewSocket = case gen_server:call(lhttpc_manager, Args, infinity) of
-                no_socket ->
+                {ok, no_socket}->
                     socket_server:connect(get_port());
                 {ok, S} ->
                     S
diff --git a/test/lhttpc_tests.erl b/test/lhttpc_tests.erl
index 7b6b809a..9ba2d29f 100644
--- a/test/lhttpc_tests.erl
+++ b/test/lhttpc_tests.erl
@@ -1,7 +1,7 @@
 %%% ----------------------------------------------------------------------------
-%%% Copyright (c) 2009, Erlang Training and Consulting Ltd.
+%%% Copyright (c) 2009-2013, Erlang Solutions Ltd.
 %%% All rights reserved.
-%%% 
+%%%
 %%% Redistribution and use in source and binary forms, with or without
 %%% modification, are permitted provided that the following conditions are met:
 %%%    * Redistributions of source code must retain the above copyright
@@ -9,14 +9,14 @@
 %%%    * Redistributions in binary form must reproduce the above copyright
 %%%      notice, this list of conditions and the following disclaimer in the
 %%%      documentation and/or other materials provided with the distribution.
-%%%    * Neither the name of Erlang Training and Consulting Ltd. nor the
+%%%    * Neither the name of Erlang Solutions Ltd. nor the
 %%%      names of its contributors may be used to endorse or promote products
 %%%      derived from this software without specific prior written permission.
-%%% 
-%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS''
+%%%
+%%% THIS SOFTWARE IS PROVIDED BY Erlang Solutions Ltd. ''AS IS''
 %%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 %%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE
+%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Solutions Ltd. BE
 %%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 %%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 %%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
@@ -32,65 +32,6 @@
 
 -include_lib("eunit/include/eunit.hrl").
 
--define(DEFAULT_STRING, "Great success!").
--define(LONG_BODY_PART,
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-        "This is a relatively long body, that we send to the client... "
-    ).
-
 test_no(N, Tests) ->
     setelement(2, Tests,
         setelement(4, element(2, Tests),
@@ -109,11 +50,12 @@ stop_app(_) ->
     ok = application:stop(ssl).
 
 tcp_test_() ->
-    {inorder, 
+    {inorder,
         {setup, fun start_app/0, fun stop_app/1, [
                 ?_test(simple_get()),
                 ?_test(simple_get_ipv6()),
                 ?_test(empty_get()),
+                ?_test(connection_refused()),
                 ?_test(basic_auth()),
                 ?_test(missing_basic_auth()),
                 ?_test(wrong_basic_auth()),
@@ -149,18 +91,18 @@ tcp_test_() ->
                 ?_test(partial_upload_chunked_no_trailer()),
                 ?_test(partial_download_illegal_option()),
                 ?_test(partial_download_identity()),
-                ?_test(partial_download_infinity_window()),
+	        ?_test(partial_download_infinity_window()),
                 ?_test(partial_download_no_content_length()),
                 ?_test(partial_download_no_content()),
                 ?_test(limited_partial_download_identity()),
                 ?_test(partial_download_chunked()),
                 ?_test(partial_download_chunked_infinite_part()),
                 ?_test(partial_download_smallish_chunks()),
-                ?_test(partial_download_slow_chunks()),
-                ?_test(close_connection()),
-                ?_test(message_queue()),
-                ?_test(trailing_space_header()),
-                ?_test(connection_count()) % just check that it's 0 (last)
+                ?_test(partial_download_slow_chunks())
+              %  ?_test(close_connection()),
+              %  ?_test(message_queue()),
+              %  ?_test(trailing_space_header()),
+              %  ?_test(connection_count()) % just check that it's 0 (last)
             ]}
     }.
 
@@ -175,15 +117,13 @@ ssl_test_() ->
             ]}
     }.
 
-other_test_() ->
-    [
-        ?_test(invalid_options())
-    ].
+options_test() ->
+    invalid_options().
 
-%%% Tests
+cookies_test() ->
+    cookies().
 
-message_queue() ->
-    receive X -> erlang:error({unexpected_message, X}) after 0 -> ok end.
+%%% Tests
 
 simple_get() ->
     simple(get),
@@ -194,16 +134,20 @@ simple_get_ipv6() ->
     simple("GET", inet6).
 
 empty_get() ->
-    Port = start(gen_tcp, [fun empty_body/5]),
+    Port = start(gen_tcp, [fun webserver_utils:empty_body/5]),
     URL = url(Port, "/empty"),
     {ok, Response} = lhttpc:request(URL, "GET", [], 1000),
     ?assertEqual({200, "OK"}, status(Response)),
     ?assertEqual(<<>>, body(Response)).
 
+connection_refused() ->
+    Response = lhttpc:request("http://127.0.0.1:50234/none", "GET", [], 100),
+    ?assertEqual({error, econnrefused}, Response).
+
 basic_auth() ->
     User = "foo",
     Passwd = "bar",
-    Port = start(gen_tcp, [basic_auth_responder(User, Passwd)]),
+    Port = start(gen_tcp, [webserver_utils:basic_auth_responder(User, Passwd)]),
     URL = url(Port, "/empty", User, Passwd),
     {ok, Response} = lhttpc:request(URL, "GET", [], 1000),
     ?assertEqual({200, "OK"}, status(Response)),
@@ -212,7 +156,7 @@ basic_auth() ->
 missing_basic_auth() ->
     User = "foo",
     Passwd = "bar",
-    Port = start(gen_tcp, [basic_auth_responder(User, Passwd)]),
+    Port = start(gen_tcp, [webserver_utils:basic_auth_responder(User, Passwd)]),
     URL = url(Port, "/empty"),
     {ok, Response} = lhttpc:request(URL, "GET", [], 1000),
     ?assertEqual({401, "Unauthorized"}, status(Response)),
@@ -221,50 +165,50 @@ missing_basic_auth() ->
 wrong_basic_auth() ->
     User = "foo",
     Passwd = "bar",
-    Port = start(gen_tcp, [basic_auth_responder(User, Passwd)]),
+    Port = start(gen_tcp, [webserver_utils:basic_auth_responder(User, Passwd)]),
     URL = url(Port, "/empty", User, "wrong_password"),
     {ok, Response} = lhttpc:request(URL, "GET", [], 1000),
     ?assertEqual({401, "Unauthorized"}, status(Response)),
     ?assertEqual(<<"wrong_auth">>, body(Response)).
 
 get_with_mandatory_hdrs() ->
-    Port = start(gen_tcp, [fun simple_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:simple_response/5]),
     URL = url(Port, "/host"),
-    Body = <>,
+    Body = list_to_binary(webserver_utils:default_string()),
     Hdrs = [
         {"content-length", integer_to_list(size(Body))},
         {"host", "localhost"}
     ],
     {ok, Response} = lhttpc:request(URL, "POST", Hdrs, Body, 1000),
     ?assertEqual({200, "OK"}, status(Response)),
-    ?assertEqual(<>, body(Response)).
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response)).
 
 get_with_mandatory_hdrs_by_atoms() ->
-    Port = start(gen_tcp, [fun simple_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:simple_response/5]),
     URL = url(Port, "/host"),
-    Body = <>,
+    Body = list_to_binary(webserver_utils:default_string()),
     Hdrs = [
         {'Content-Length', integer_to_list(size(Body))},
         {'Host', "localhost"}
     ],
     {ok, Response} = lhttpc:request(URL, "POST", Hdrs, Body, 1000),
     ?assertEqual({200, "OK"}, status(Response)),
-    ?assertEqual(<>, body(Response)).
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response)).
 
 get_with_mandatory_hdrs_by_binaries() ->
-    Port = start(gen_tcp, [fun simple_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:simple_response/5]),
     URL = url(Port, "/host"),
-    Body = <>,
+    Body = list_to_binary(webserver_utils:default_string()),
     Hdrs = [
         {<<"Content-Length">>, integer_to_list(size(Body))},
         {<<"Host">>, "localhost"}
     ],
     {ok, Response} = lhttpc:request(URL, "POST", Hdrs, Body, 1000),
     ?assertEqual({200, "OK"}, status(Response)),
-    ?assertEqual(<>, body(Response)).
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response)).
 
 get_with_connect_options() ->
-    Port = start(gen_tcp, [fun empty_body/5]),
+    Port = start(gen_tcp, [fun webserver_utils:empty_body/5]),
     URL = url(Port, "/empty"),
     Options = [{connect_options, [{ip, {127, 0, 0, 1}}, {port, 0}]}],
     {ok, Response} = lhttpc:request(URL, "GET", [], [], 1000, Options),
@@ -272,24 +216,24 @@ get_with_connect_options() ->
     ?assertEqual(<<>>, body(Response)).
 
 no_content_length() ->
-    Port = start(gen_tcp, [fun no_content_length/5]),
+    Port = start(gen_tcp, [fun webserver_utils:no_content_length/5]),
     URL = url(Port, "/no_cl"),
     {ok, Response} = lhttpc:request(URL, "GET", [], 1000),
     ?assertEqual({200, "OK"}, status(Response)),
-    ?assertEqual(<>, body(Response)).
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response)).
 
 no_content_length_1_0() ->
-    Port = start(gen_tcp, [fun no_content_length_1_0/5]),
+    Port = start(gen_tcp, [fun webserver_utils:no_content_length_1_0/5]),
     URL = url(Port, "/no_cl"),
     {ok, Response} = lhttpc:request(URL, "GET", [], 1000),
     ?assertEqual({200, "OK"}, status(Response)),
-    ?assertEqual(<>, body(Response)).
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response)).
 
 %% Check the header value is trimming spaces on header values
 %% which can cause crash in lhttpc_client:body_type when Content-Length
 %% is converted from list to integer
 trailing_space_header() ->
-    Port = start(gen_tcp, [fun trailing_space_header/5]),
+    Port = start(gen_tcp, [fun webserver_utils:trailing_space_header/5]),
     URL = url(Port, "/no_cl"),
     {ok, Response} = lhttpc:request(URL, "GET", [], 1000),
     Headers = headers(Response),
@@ -297,65 +241,65 @@ trailing_space_header() ->
     ?assertEqual("14", ContentLength).
 
 get_not_modified() ->
-    Port = start(gen_tcp, [fun not_modified_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:not_modified_response/5]),
     URL = url(Port, "/not_modified"),
     {ok, Response} = lhttpc:request(URL, "GET", [], [], 1000),
     ?assertEqual({304, "Not Modified"}, status(Response)),
     ?assertEqual(<<>>, body(Response)).
 
 simple_head() ->
-    Port = start(gen_tcp, [fun head_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:head_response/5]),
     URL = url(Port, "/HEAD"),
     {ok, Response} = lhttpc:request(URL, "HEAD", [], 1000),
     ?assertEqual({200, "OK"}, status(Response)),
     ?assertEqual(<<>>, body(Response)).
 
 simple_head_atom() ->
-    Port = start(gen_tcp, [fun head_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:head_response/5]),
     URL = url(Port, "/head"),
     {ok, Response} = lhttpc:request(URL, head, [], 1000),
     ?assertEqual({200, "OK"}, status(Response)),
     ?assertEqual(<<>>, body(Response)).
 
 delete_no_content() ->
-    Port = start(gen_tcp, [fun no_content_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:no_content_response/5]),
     URL = url(Port, "/delete_no_content"),
     {ok, Response} = lhttpc:request(URL, delete, [], 1000),
     ?assertEqual({204, "OK"}, status(Response)),
     ?assertEqual(<<>>, body(Response)).
 
 delete_content() ->
-    Port = start(gen_tcp, [fun simple_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:simple_response/5]),
     URL = url(Port, "/delete_content"),
     {ok, Response} = lhttpc:request(URL, "DELETE", [], 1000),
     ?assertEqual({200, "OK"}, status(Response)),
-    ?assertEqual(<>, body(Response)).
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response)).
 
 options_no_content() ->
-    Port = start(gen_tcp, [fun head_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:head_response/5]),
     URL = url(Port, "/options_no_content"),
     {ok, Response} = lhttpc:request(URL, "OPTIONS", [], 1000),
     ?assertEqual({200, "OK"}, status(Response)),
     ?assertEqual(<<>>, body(Response)).
 
 options_content() ->
-    Port = start(gen_tcp, [fun simple_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:simple_response/5]),
     URL = url(Port, "/options_content"),
     {ok, Response} = lhttpc:request(URL, "OPTIONS", [], 1000),
     ?assertEqual({200, "OK"}, status(Response)),
-    ?assertEqual(<>, body(Response)).
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response)).
 
 server_connection_close() ->
-    Port = start(gen_tcp, [fun respond_and_close/5]),
+    Port = start(gen_tcp, [fun webserver_utils:respond_and_close/5]),
     URL = url(Port, "/close"),
     Body = pid_to_list(self()),
     {ok, Response} = lhttpc:request(URL, "PUT", [], Body, 1000),
     ?assertEqual({200, "OK"}, status(Response)),
-    ?assertEqual(<>, body(Response)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response)),
     receive closed -> ok end.
 
 client_connection_close() ->
-    Port = start(gen_tcp, [fun respond_and_wait/5]),
+    Port = start(gen_tcp, [fun webserver_utils:respond_and_wait/5]),
     URL = url(Port, "/close"),
     Body = pid_to_list(self()),
     Hdrs = [{"Connection", "close"}],
@@ -364,7 +308,7 @@ client_connection_close() ->
     receive closed -> ok end.
 
 pre_1_1_server_connection() ->
-    Port = start(gen_tcp, [fun pre_1_1_server/5]),
+    Port = start(gen_tcp, [fun webserver_utils:pre_1_1_server/5]),
     URL = url(Port, "/close"),
     Body = pid_to_list(self()),
     {ok, _} = lhttpc:request(URL, put, [], Body, 1000),
@@ -375,17 +319,18 @@ pre_1_1_server_connection() ->
 
 pre_1_1_server_keep_alive() ->
     Port = start(gen_tcp, [
-            fun pre_1_1_server_keep_alive/5,
-            fun pre_1_1_server/5
+            fun webserver_utils:pre_1_1_server_keep_alive/5,
+            fun webserver_utils:pre_1_1_server/5
         ]),
     URL = url(Port, "/close"),
     Body = pid_to_list(self()),
-    {ok, Response1} = lhttpc:request(URL, get, [], [], 1000),
-    {ok, Response2} = lhttpc:request(URL, put, [], Body, 1000),
+    %this test need to use a client now (or a pool).
+    {ok, Response1} = lhttpc:request(URL, get, [], [], 1000, [{pool_options, [{pool_ensure, true}, {pool, pool_name}]}]),
+    {ok, Response2} = lhttpc:request(URL, put, [], Body, 1000, [{pool_options, [{pool_ensure, true}, {pool, pool_name}]}]),
     ?assertEqual({200, "OK"}, status(Response1)),
     ?assertEqual({200, "OK"}, status(Response2)),
-    ?assertEqual(<>, body(Response1)),
-    ?assertEqual(<>, body(Response2)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response1)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response2)),
     % Wait for the server to see that socket has been closed.
     % The socket should be closed by us since the server responded with a
     % 1.0 version, and not the Connection: keep-alive header.
@@ -396,7 +341,7 @@ simple_put() ->
     simple("PUT").
 
 post() ->
-    Port = start(gen_tcp, [fun copy_body/5]),
+    Port = start(gen_tcp, [fun webserver_utils:copy_body/5]),
     URL = url(Port, "/post"),
     {X, Y, Z} = now(),
     Body = [
@@ -412,7 +357,7 @@ post() ->
     ?assertEqual(iolist_to_binary(Body), body(Response)).
 
 post_100_continue() ->
-    Port = start(gen_tcp, [fun copy_body_100_continue/5]),
+    Port = start(gen_tcp, [fun webserver_utils:copy_body_100_continue/5]),
     URL = url(Port, "/post"),
     {X, Y, Z} = now(),
     Body = [
@@ -432,64 +377,66 @@ bad_url() ->
 
 persistent_connection() ->
     Port = start(gen_tcp, [
-            fun simple_response/5,
-            fun simple_response/5,
-            fun copy_body/5
+            fun webserver_utils:simple_response/5,
+            fun webserver_utils:simple_response/5,
+            fun webserver_utils:copy_body/5
         ]),
     URL = url(Port, "/persistent"),
-    {ok, FirstResponse} = lhttpc:request(URL, "GET", [], 1000),
+    {ok, FirstResponse} = lhttpc:request(URL, "GET", [], [], 1000, [{pool_options, [{pool_ensure, true}, {pool, pool_name}]}]),
     Headers = [{"KeepAlive", "300"}], % shouldn't be needed
-    {ok, SecondResponse} = lhttpc:request(URL, "GET", Headers, 1000),
-    {ok, ThirdResponse} = lhttpc:request(URL, "POST", [], 1000),
+    {ok, SecondResponse} = lhttpc:request(URL, "GET", Headers, [], 1000, [{pool_options, [{pool_ensure, true}, {pool, pool_name}]}]),
+    {ok, ThirdResponse} = lhttpc:request(URL, "POST", [], [], 1000, [{pool_options, [{pool_ensure, true}, {pool, pool_name}]}]),
     ?assertEqual({200, "OK"}, status(FirstResponse)),
-    ?assertEqual(<>, body(FirstResponse)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(FirstResponse)),
     ?assertEqual({200, "OK"}, status(SecondResponse)),
-    ?assertEqual(<>, body(SecondResponse)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(SecondResponse)),
     ?assertEqual({200, "OK"}, status(ThirdResponse)),
     ?assertEqual(<<>>, body(ThirdResponse)).
 
 request_timeout() ->
-    Port = start(gen_tcp, [fun very_slow_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:very_slow_response/5]),
     URL = url(Port, "/slow"),
     ?assertEqual({error, timeout}, lhttpc:request(URL, get, [], 50)).
 
 connection_timeout() ->
-    Port = start(gen_tcp, [fun simple_response/5, fun simple_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:simple_response/5, fun webserver_utils:simple_response/5]),
     URL = url(Port, "/close_conn"),
+    {ok, _PoolManager} = lhttpc:add_pool(lhttpc_manager),
     lhttpc_manager:update_connection_timeout(lhttpc_manager, 50), % very short keep alive
     {ok, Response} = lhttpc:request(URL, get, [], 100),
     ?assertEqual({200, "OK"}, status(Response)),
-    ?assertEqual(<>, body(Response)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response)),
     timer:sleep(100),
     ?assertEqual(0,
         lhttpc_manager:connection_count(lhttpc_manager, {"localhost", Port, false})),
     lhttpc_manager:update_connection_timeout(lhttpc_manager, 300000). % set back
 
 suspended_manager() ->
-    Port = start(gen_tcp, [fun simple_response/5, fun simple_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:simple_response/5, fun webserver_utils:simple_response/5]),
     URL = url(Port, "/persistent"),
-    {ok, FirstResponse} = lhttpc:request(URL, get, [], 50),
+    {ok, FirstResponse} = lhttpc:request(URL, get, [], [], 50, [{pool_options, [{pool_ensure, true}, {pool, lhttpc_manager}]}]),
     ?assertEqual({200, "OK"}, status(FirstResponse)),
-    ?assertEqual(<>, body(FirstResponse)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(FirstResponse)),
     Pid = whereis(lhttpc_manager),
     true = erlang:suspend_process(Pid),
-    ?assertEqual({error, timeout}, lhttpc:request(URL, get, [], 50)),
+    ?assertEqual({error, connection_timeout}, lhttpc:request(URL, get, [], [], 50, [{pool_options, [{pool_ensure, true}, {pool, lhttpc_manager}]}])),
     true = erlang:resume_process(Pid),
     ?assertEqual(1,
         lhttpc_manager:connection_count(lhttpc_manager, {"localhost", Port, false})),
-    {ok, SecondResponse} = lhttpc:request(URL, get, [], 50),
+    {ok, SecondResponse} = lhttpc:request(URL, get, [], [], 50, [{pool_options, [{pool_ensure, true}, {pool, lhttpc_manager}]}]),
     ?assertEqual({200, "OK"}, status(SecondResponse)),
-    ?assertEqual(<>, body(SecondResponse)).
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(SecondResponse)).
 
 chunked_encoding() ->
-    Port = start(gen_tcp, [fun chunked_response/5, fun chunked_response_t/5]),
+    Port = start(gen_tcp, [fun webserver_utils:chunked_response/5, fun webserver_utils:chunked_response_t/5]),
     URL = url(Port, "/chunked"),
-    {ok, FirstResponse} = lhttpc:request(URL, get, [], 50),
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    {ok, FirstResponse} = lhttpc:request_client(Client, URL, get, [], 1000),
     ?assertEqual({200, "OK"}, status(FirstResponse)),
-    ?assertEqual(<>, body(FirstResponse)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(FirstResponse)),
     ?assertEqual("chunked", lhttpc_lib:header_value("transfer-encoding",
             headers(FirstResponse))),
-    {ok, SecondResponse} = lhttpc:request(URL, get, [], 50),
+    {ok, SecondResponse} = lhttpc:request_client(Client, URL, get, [], 1000),
     ?assertEqual({200, "OK"}, status(SecondResponse)),
     ?assertEqual(<<"Again, great success!">>, body(SecondResponse)),
     ?assertEqual("ChUnKeD", lhttpc_lib:header_value("transfer-encoding",
@@ -500,258 +447,284 @@ chunked_encoding() ->
             headers(SecondResponse))).
 
 partial_upload_identity() ->
-    Port = start(gen_tcp, [fun simple_response/5, fun simple_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:simple_response/5, fun webserver_utils:simple_response/5]),
     URL = url(Port, "/partial_upload"),
     Body = [<<"This">>, <<" is ">>, <<"chunky">>, <<" stuff!">>],
     Hdrs = [{"Content-Length", integer_to_list(iolist_size(Body))}],
-    Options = [{partial_upload, 1}],
-    {ok, UploadState1} = lhttpc:request(URL, post, Hdrs, hd(Body), 1000, Options),
-    Response1 = lists:foldl(fun upload_parts/2, UploadState1,
-        tl(Body) ++ [http_eob]),
+    Options = [{partial_upload, true}],
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    {ok, partial_upload} =
+        lhttpc:request_client(Client, URL, post, Hdrs, hd(Body), 1000, Options),
+    {ok, Response1} = upload_parts(Client, tl(Body) ++ [http_eob]),
     ?assertEqual({200, "OK"}, status(Response1)),
-    ?assertEqual(<>, body(Response1)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response1)),
     ?assertEqual("This is chunky stuff!",
         lhttpc_lib:header_value("x-test-orig-body", headers(Response1))),
     % Make sure it works with no body part in the original request as well
-    {ok, UploadState2} = lhttpc:request(URL, post, Hdrs, [], 1000, Options),
-    Response2 = lists:foldl(fun upload_parts/2, UploadState2,
-        Body ++ [http_eob]),
+    {ok, partial_upload} = lhttpc:request_client(Client, URL, post, Hdrs, [], 1000, Options),
+    {ok, Response2} = upload_parts(Client, Body ++ [http_eob]),
     ?assertEqual({200, "OK"}, status(Response2)),
-    ?assertEqual(<>, body(Response2)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response2)),
     ?assertEqual("This is chunky stuff!",
         lhttpc_lib:header_value("x-test-orig-body", headers(Response2))).
 
 partial_upload_identity_iolist() ->
-    Port = start(gen_tcp, [fun simple_response/5, fun simple_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:simple_response/5, fun webserver_utils:simple_response/5]),
     URL = url(Port, "/partial_upload"),
     Body = ["This", [<<" ">>, $i, $s, [" "]], <<"chunky">>, [<<" stuff!">>]],
     Hdrs = [{"Content-Length", integer_to_list(iolist_size(Body))}],
-    Options = [{partial_upload, 1}],
-    {ok, UploadState1} = lhttpc:request(URL, post, Hdrs, hd(Body), 1000, Options),
-    Response1 = lists:foldl(fun upload_parts/2, UploadState1,
-        tl(Body) ++ [http_eob]),
+    Options = [{partial_upload, true}],
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    {ok, partial_upload} =
+        lhttpc:request_client(Client, URL, post, Hdrs, hd(Body), 1000, Options),
+    {ok, Response1} = upload_parts(Client, tl(Body) ++ [http_eob]),
     ?assertEqual({200, "OK"}, status(Response1)),
-    ?assertEqual(<>, body(Response1)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response1)),
     ?assertEqual("This is chunky stuff!",
         lhttpc_lib:header_value("x-test-orig-body", headers(Response1))),
     % Make sure it works with no body part in the original request as well
-    {ok, UploadState2} = lhttpc:request(URL, post, Hdrs, [], 1000, Options),
-    Response2 = lists:foldl(fun upload_parts/2, UploadState2,
-        Body ++ [http_eob]),
+    {ok, _UploadState2} = lhttpc:request_client(Client, URL, post, Hdrs, [], 1000, Options),
+    {ok, Response2} = upload_parts(Client, Body ++ [http_eob]),
     ?assertEqual({200, "OK"}, status(Response2)),
-    ?assertEqual(<>, body(Response2)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response2)),
     ?assertEqual("This is chunky stuff!",
         lhttpc_lib:header_value("x-test-orig-body", headers(Response2))).
 
 partial_upload_chunked() ->
-    Port = start(gen_tcp, [fun chunked_upload/5, fun chunked_upload/5]),
+    Port = start(gen_tcp, [fun webserver_utils:chunked_upload/5, fun webserver_utils:chunked_upload/5]),
     URL = url(Port, "/partial_upload_chunked"),
     Body = ["This", [<<" ">>, $i, $s, [" "]], <<"chunky">>, [<<" stuff!">>]],
-    Options = [{partial_upload, 1}],
-    {ok, UploadState1} = lhttpc:request(URL, post, [], hd(Body), 1000, Options),
+    Options = [{partial_upload, true}],
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    {ok, partial_upload} = lhttpc:request_client(Client, URL, post, [], hd(Body), 1000, Options),
     Trailer = {"X-Trailer-1", "my tail is tailing me...."},
-    {ok, Response1} = lhttpc:send_trailers(
-        lists:foldl(fun upload_parts/2, UploadState1, tl(Body)),
-        [Trailer]
-    ),
+    upload_parts(Client, tl(Body)),
+    {ok, Response1} = lhttpc:send_trailers(Client, [Trailer]),
     ?assertEqual({200, "OK"}, status(Response1)),
-    ?assertEqual(<>, body(Response1)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response1)),
     ?assertEqual("This is chunky stuff!",
         lhttpc_lib:header_value("x-test-orig-body", headers(Response1))),
-    ?assertEqual(element(2, Trailer), 
+    ?assertEqual(element(2, Trailer),
         lhttpc_lib:header_value("x-test-orig-trailer-1", headers(Response1))),
     % Make sure it works with no body part in the original request as well
     Headers = [{"Transfer-Encoding", "chunked"}],
-    {ok, UploadState2} = lhttpc:request(URL, post, Headers, [], 1000, Options),
-    {ok, Response2} = lhttpc:send_trailers(
-        lists:foldl(fun upload_parts/2, UploadState2, Body),
-        [Trailer]
-    ),
+    {ok, partial_upload} = lhttpc:request_client(Client, URL, post, Headers, [], 1000, Options),
+    upload_parts(Client, Body),
+    {ok, Response2} = lhttpc:send_trailers(Client, [Trailer]),
     ?assertEqual({200, "OK"}, status(Response2)),
-    ?assertEqual(<>, body(Response2)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response2)),
     ?assertEqual("This is chunky stuff!",
         lhttpc_lib:header_value("x-test-orig-body", headers(Response2))),
-    ?assertEqual(element(2, Trailer), 
+    ?assertEqual(element(2, Trailer),
         lhttpc_lib:header_value("x-test-orig-trailer-1", headers(Response2))).
 
 partial_upload_chunked_no_trailer() ->
-    Port = start(gen_tcp, [fun chunked_upload/5]),
+    Port = start(gen_tcp, [fun webserver_utils:chunked_upload/5]),
     URL = url(Port, "/partial_upload_chunked_no_trailer"),
     Body = [<<"This">>, <<" is ">>, <<"chunky">>, <<" stuff!">>],
-    Options = [{partial_upload, 1}],
-    {ok, UploadState1} = lhttpc:request(URL, post, [], hd(Body), 1000, Options),
-    {ok, Response} = lhttpc:send_body_part(
-        lists:foldl(fun upload_parts/2, UploadState1, tl(Body)),
-        http_eob
-    ),
+    Options = [{partial_upload, true}],
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    {ok, partial_upload} = lhttpc:request_client(Client, URL, post, [], hd(Body), 1000, Options),
+    ok = upload_parts(Client, tl(Body)),
+    {ok, Response} = lhttpc:send_body_part(Client, http_eob, 1000),
     ?assertEqual({200, "OK"}, status(Response)),
-    ?assertEqual(<>, body(Response)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response)),
     ?assertEqual("This is chunky stuff!",
-        lhttpc_lib:header_value("x-test-orig-body", headers(Response))).
+                    lhttpc_lib:header_value("x-test-orig-body", headers(Response))).
 
 partial_download_illegal_option() ->
     ?assertError({bad_option, {partial_download, {foo, bar}}},
         lhttpc:request("http://localhost/", get, [], <<>>, 1000,
             [{partial_download, [{foo, bar}]}])).
 
+long_body_part(Size) ->
+    list_to_binary(
+      lists:flatten(
+    [webserver_utils:long_body_part() || _ <- lists:seq(1, Size)])).
+
 partial_download_identity() ->
-    Port = start(gen_tcp, [fun large_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:large_response/5]),
     URL = url(Port, "/partial_download_identity"),
     PartialDownload = [
-        {window_size, 1}
+        {window_size, 1},
+        {recv_proc, self()}
     ],
     Options = [{partial_download, PartialDownload}],
-    {ok, {Status, _, Pid}} =
-        lhttpc:request(URL, get, [], <<>>, 1000, Options),
-    Body = read_partial_body(Pid),
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    {ok, {Status, _Hdrs, partial_download}} =
+        lhttpc:request_client(Client, URL, get, [], <<>>, 1000, Options),
+    Body = read_partial_body(Client),
     ?assertEqual({200, "OK"}, Status),
-    ?assertEqual(<>, Body).
+    ?assertEqual(size(long_body_part(3)), size(Body)),
+    ?assertEqual(long_body_part(3), Body).
 
 partial_download_infinity_window() ->
-    Port = start(gen_tcp, [fun large_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:large_response/5]),
     URL = url(Port, "/partial_download_identity"),
     PartialDownload = [
-        {window_size, infinity}
+               {window_size, infinity},
+               {recv_proc, self()}
     ],
     Options = [{partial_download, PartialDownload}],
-    {ok, {Status, _, Pid}} = lhttpc:request(URL, get, [], <<>>, 1000, Options),
-    Body = read_partial_body(Pid),
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    {ok, {Status, _Hdrs, partial_download}} =
+        lhttpc:request_client(Client, URL, get, [], <<>>, 1000, Options),
+    Body = read_partial_body(Client),
     ?assertEqual({200, "OK"}, Status),
-    ?assertEqual(<>, Body).
+    ?assertEqual(long_body_part(3), Body).
 
 partial_download_no_content_length() ->
-    Port = start(gen_tcp, [fun no_content_length/5]),
+    Port = start(gen_tcp, [fun webserver_utils:no_content_length/5]),
     URL = url(Port, "/no_cl"),
     PartialDownload = [
-        {window_size, 1}
+               {window_size, 1},
+               {recv_proc, self()}
     ],
     Options = [{partial_download, PartialDownload}],
-    {ok, {Status, _, Pid}} = lhttpc:request(URL, get, [], <<>>, 1000, Options),
-    Body = read_partial_body(Pid),
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    {ok, {Status, _Hdrs, partial_download}} =
+        lhttpc:request_client(Client, URL, get, [], <<>>, 1000, Options),
+    Body = read_partial_body(Client),
     ?assertEqual({200, "OK"}, Status),
-    ?assertEqual(<>, Body).
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), Body).
 
 partial_download_no_content() ->
-    Port = start(gen_tcp, [fun no_content_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:no_content_response/5]),
     URL = url(Port, "/partial_download_identity"),
     PartialDownload = [
-        {window_size, 1}
+               {window_size, 1},
+               {recv_proc, self()}
     ],
     Options = [{partial_download, PartialDownload}],
-    {ok, {Status, _, Body}} =
-        lhttpc:request(URL, get, [], <<>>, 1000, Options),
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    {ok, {Status, _Hdrs, Body}} =
+        lhttpc:request_client(Client, URL, get, [], <<>>, 1000, Options),
     ?assertEqual({204, "OK"}, Status),
     ?assertEqual(undefined, Body).
 
 limited_partial_download_identity() ->
-    Port = start(gen_tcp, [fun large_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:large_response/5]),
     URL = url(Port, "/partial_download_identity"),
     PartialDownload = [
-        {window_size, 1},
-        {part_size, 512} % bytes
-    ],
+               {window_size, 1},
+               {part_size, 512}, % bytes
+               {recv_proc, self()}
+              ],
     Options = [{partial_download, PartialDownload}],
-    {ok, {Status, _, Pid}} =
-        lhttpc:request(URL, get, [], <<>>, 1000, Options),
-    Body = read_partial_body(Pid, 512),
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    {ok, {Status, _Hdrs, partial_download}} =
+        lhttpc:request_client(Client, URL, get, [], <<>>, 1000, Options),
+    Body = read_partial_body(Client, 512),
     ?assertEqual({200, "OK"}, Status),
-    ?assertEqual(<>, Body).
+    ?assertEqual(long_body_part(3), Body).
 
 partial_download_chunked() ->
-    Port = start(gen_tcp, [fun large_chunked_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:large_chunked_response/5]),
     URL = url(Port, "/partial_download_identity"),
     PartialDownload = [
-        {window_size, 1},
-        {part_size, length(?LONG_BODY_PART) * 3}
+               {window_size, 1},
+               {part_size, length(webserver_utils:long_body_part()) * 3},
+               {recv_proc, self()}
     ],
     Options = [{partial_download, PartialDownload}],
-    {ok, {Status, _, Pid}} =
-        lhttpc:request(URL, get, [], <<>>, 1000, Options),
-    Body = read_partial_body(Pid),
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    {ok, {Status, _Hdrs, partial_download}} =
+        lhttpc:request_client(Client, URL, get, [], <<>>, 1000, Options),
+    Body = read_partial_body(Client),
     ?assertEqual({200, "OK"}, Status),
-    ?assertEqual(<>, Body).
+    ?assertEqual(long_body_part(3), Body).
 
 partial_download_chunked_infinite_part() ->
-    Port = start(gen_tcp, [fun large_chunked_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:large_chunked_response/5]),
     URL = url(Port, "/partial_download_identity"),
     PartialDownload = [
-        {window_size, 1},
-        {part_size, infinity}
+               {window_size, 1},
+               {part_size, infinity},
+               {recv_proc, self()}
     ],
     Options = [{partial_download, PartialDownload}],
-    {ok, {Status, _, Pid}} =
-        lhttpc:request(URL, get, [], <<>>, 1000, Options),
-    Body = read_partial_body(Pid),
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    {ok, {Status, _Hdrs, partial_download}} =
+        lhttpc:request_client(Client, URL, get, [], <<>>, 1000, Options),
+    Body = read_partial_body(Client),
     ?assertEqual({200, "OK"}, Status),
-    ?assertEqual(<>, Body).
+    ?assertEqual(size(long_body_part(3)), size(Body)),
+    ?assertEqual(long_body_part(3), Body).
 
 partial_download_smallish_chunks() ->
-    Port = start(gen_tcp, [fun large_chunked_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:large_chunked_response/5]),
     URL = url(Port, "/partial_download_identity"),
     PartialDownload = [
-        {window_size, 1},
-        {part_size, length(?LONG_BODY_PART) - 1}
+               {window_size, 1},
+               {part_size, length(webserver_utils:long_body_part()) - 1},
+               {recv_proc, self()}
     ],
     Options = [{partial_download, PartialDownload}],
-    {ok, {Status, _, Pid}} =
-        lhttpc:request(URL, get, [], <<>>, 1000, Options),
-    Body = read_partial_body(Pid),
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    {ok, {Status, _Hdrs, partial_download}} =
+        lhttpc:request_client(Client, URL, get, [], <<>>, 1000, Options),
+    Body = read_partial_body(Client),
     ?assertEqual({200, "OK"}, Status),
-    ?assertEqual(<>, Body).
+    ?assertEqual(long_body_part(3), Body).
 
 partial_download_slow_chunks() ->
-    Port = start(gen_tcp, [fun slow_chunked_response/5]),
+    Port = start(gen_tcp, [fun webserver_utils:slow_chunked_response/5]),
     URL = url(Port, "/slow"),
     PartialDownload = [
-        {window_size, 1},
-        {part_size, length(?LONG_BODY_PART) div 2}
+               {window_size, 1},
+               {part_size, length(webserver_utils:long_body_part()) div 2},
+               {recv_proc, self()}
     ],
     Options = [{partial_download, PartialDownload}],
-    {ok, {Status, _, Pid}} = lhttpc:request(URL, get, [], <<>>, 1000, Options),
-    Body = read_partial_body(Pid),
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    {ok, {Status, _Hdrs, partial_download}} =
+        lhttpc:request_client(Client, URL, get, [], <<>>, 1000, Options),
+    Body = read_partial_body(Client),
     ?assertEqual({200, "OK"}, Status),
-    ?assertEqual(<>, Body).
+    ?assertEqual(long_body_part(2), Body).
 
 close_connection() ->
-    Port = start(gen_tcp, [fun close_connection/5]),
+    Port = start(gen_tcp, [fun webserver_utils:close_connection/5]),
     URL = url(Port, "/close"),
-    ?assertEqual({error, connection_closed}, lhttpc:request(URL, "GET", [],
+    ?assertEqual({error, closed}, lhttpc:request(URL, "GET", [],
             1000)).
 
 ssl_get() ->
-    Port = start(ssl, [fun simple_response/5]),
+    Port = start(ssl, [fun webserver_utils:simple_response/5]),
     URL = ssl_url(Port, "/simple"),
-    {ok, Response} = lhttpc:request(URL, "GET", [], 1000),
+    {ok, _PoolManager} = lhttpc:add_pool(lhttpc_manager),
+    {ok, Response} = lhttpc:request(URL, "GET", [], [], 1000, [{pool_options, [{pool_ensure, true}, {pool, lhttpc_manager}]}]),
     ?assertEqual({200, "OK"}, status(Response)),
-    ?assertEqual(<>, body(Response)).
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response)).
 
 ssl_get_ipv6() ->
-    Port = start(ssl, [fun simple_response/5], inet6),
+    Port = start(ssl, [fun webserver_utils:simple_response/5], inet6),
     URL = ssl_url(inet6, Port, "/simple"),
-    {ok, Response} = lhttpc:request(URL, "GET", [], 1000),
+    {ok, Response} = lhttpc:request(URL, "GET", [], [], 1000, [{pool_options, [{pool_ensure, true}, {pool, lhttpc_manager}]}]),
     ?assertEqual({200, "OK"}, status(Response)),
-    ?assertEqual(<>, body(Response)).
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response)).
 
 ssl_post() ->
-    Port = start(ssl, [fun copy_body/5]),
+    Port = start(ssl, [fun webserver_utils:copy_body/5]),
     URL = ssl_url(Port, "/simple"),
     Body = "SSL Test 
-    Port = start(ssl, [fun chunked_response/5, fun chunked_response_t/5]),
+    Port = start(ssl, [fun webserver_utils:chunked_response/5, fun webserver_utils:chunked_response_t/5]),
     URL = ssl_url(Port, "/ssl_chunked"),
-    FirstResult = lhttpc:request(URL, get, [], 100),
+    {ok, Client} = lhttpc:connect_client(URL, []),
+    FirstResult = lhttpc:request_client(Client, URL, get, [], [], 100, [{pool_options, [{pool_ensure, true}, {pool, lhttpc_manager}]}]),
     ?assertMatch({ok, _}, FirstResult),
     {ok, FirstResponse} = FirstResult,
     ?assertEqual({200, "OK"}, status(FirstResponse)),
-    ?assertEqual(<>, body(FirstResponse)),
+    ?assertEqual(list_to_binary(webserver_utils:default_string()), body(FirstResponse)),
     ?assertEqual("chunked", lhttpc_lib:header_value("transfer-encoding",
             headers(FirstResponse))),
-    SecondResult = lhttpc:request(URL, get, [], 100),
+    SecondResult = lhttpc:request_client(Client, URL, get, [], [], 100, [{pool_options, [{pool_ensure, true}, {pool, lhttpc_manager}]}]),
     {ok, SecondResponse} = SecondResult,
     ?assertEqual({200, "OK"}, status(SecondResponse)),
     ?assertEqual(<<"Again, great success!">>, body(SecondResponse)),
@@ -774,38 +747,63 @@ invalid_options() ->
         lhttpc:request("http://localhost/", get, [], <<>>, 1000,
             [{foo, bar}, bad_option])).
 
+cookies() ->
+    Port = start(gen_tcp, [fun webserver_utils:set_cookie_response/5, fun webserver_utils:expired_cookie_response/5,
+            fun webserver_utils:receive_right_cookies/5]),
+    URL = url(Port, "/cookies"),
+    Options = [{use_cookies, true}],
+    {ok, Client} = lhttpc:connect_client(URL, Options),
+    {ok, Response1} = lhttpc:request_client(Client, URL, get, [], 1000),
+    ?assertEqual({200, "OK"}, status(Response1)),
+    {ok, Response2} = lhttpc:request_client(Client, URL, get, [], 1000),
+    ?assertEqual({200, "OK"}, status(Response2)),
+    {ok, Response3} = lhttpc:request_client(Client, URL, get, [], 1000),
+    ?assertEqual({200, "OK"}, status(Response3)).
 
-%%% Helpers functions
-
-upload_parts(BodyPart, CurrentState) ->
-    {ok, NextState} = lhttpc:send_body_part(CurrentState, BodyPart, 1000),
-    NextState.
-
-read_partial_body(Pid) ->
-    read_partial_body(Pid, infinity, []).
-
-read_partial_body(Pid, Size) ->
-    read_partial_body(Pid, Size, []).
 
-read_partial_body(Pid, Size, Acc) ->
-    case lhttpc:get_body_part(Pid) of
-        {ok, {http_eob, []}} ->
-            list_to_binary(Acc);
-        {ok, Bin} ->
-            if
+%%% Helpers functions
+upload_parts(Client, Parts) ->
+    lists:foldl(fun(BodyPart, _) ->
+            lhttpc:send_body_part(Client, BodyPart, 1000)
+        end, ok, Parts).
+
+read_partial_body(Client) ->
+    read_partial_body(Client, infinity, <<>>).
+
+read_partial_body(Client, Size) ->
+    read_partial_body(Client, Size, <<>>).
+
+read_partial_body(Client, Size, Acc) ->
+    receive
+    {body_part,  http_eob} ->
+        %% chunked download
+        ok = lhttpc:get_body_part(Client),
+        read_partial_body(Client, Size, Acc);
+    {body_part,  window_finished} ->
+        ok = lhttpc:get_body_part(Client),
+        read_partial_body(Client, Size, Acc);
+    {body_part, Bin} ->
+        if
                 Size =:= infinity ->
                     ok;
                 Size =/= infinity ->
                     ?assert(Size >= iolist_size(Bin))
-            end,
-            read_partial_body(Pid, Size, [Acc, Bin])
+        end,
+        read_partial_body(Client, Size, <>);
+    {http_eob, _Trailers} ->
+        Acc;
+    {body_part_error, Reason} ->
+        {error, Reason, Acc}
+    after
+    1000 ->
+        {error, receive_clause, Acc}
     end.
 
 simple(Method) ->
     simple(Method, inet).
 
 simple(Method, Family) ->
-    case start(gen_tcp, [fun simple_response/5], Family) of
+    case start(gen_tcp, [fun webserver_utils:simple_response/5], Family) of
         {error, family_not_supported} when Family =:= inet6 ->
             % Localhost has no IPv6 support - not a big issue.
             ?debugMsg("WARNING: impossible to test IPv6 support~n");
@@ -815,7 +813,7 @@ simple(Method, Family) ->
             {StatusCode, ReasonPhrase} = status(Response),
             ?assertEqual(200, StatusCode),
             ?assertEqual("OK", ReasonPhrase),
-            ?assertEqual(<>, body(Response))
+            ?assertEqual(list_to_binary(webserver_utils:default_string()), body(Response))
     end.
 
 url(Port, Path) ->
@@ -850,297 +848,3 @@ body({_, _, Body}) ->
 
 headers({_, Headers, _}) ->
     Headers.
-
-%%% Responders
-simple_response(Module, Socket, _Request, _Headers, Body) ->
-    Module:send(
-        Socket,
-        [
-            "HTTP/1.1 200 OK\r\n"
-            "Content-type: text/plain\r\nContent-length: 14\r\n"
-            "X-Test-Orig-Body: ", Body, "\r\n\r\n"
-            ?DEFAULT_STRING
-        ]
-    ).
-
-large_response(Module, Socket, _, _, _) ->
-    BodyPart = <>,
-    ContentLength = 3 * size(BodyPart),
-    Module:send(
-        Socket,
-        [
-            "HTTP/1.1 200 OK\r\n"
-            "Content-type: text/plain\r\n"
-            "Content-length: ", integer_to_list(ContentLength), "\r\n\r\n"
-        ]
-    ),
-    Module:send(Socket, BodyPart),
-    Module:send(Socket, BodyPart),
-    Module:send(Socket, BodyPart).
-
-large_chunked_response(Module, Socket, _, _, _) ->
-    BodyPart = <>,
-    ChunkSize = erlang:integer_to_list(size(BodyPart), 16),
-    Chunk = [ChunkSize, "\r\n", BodyPart, "\r\n"],
-    Module:send(
-        Socket,
-        [
-            "HTTP/1.1 200 OK\r\n"
-            "Content-type: text/plain\r\n"
-            "Transfer-Encoding: chunked\r\n\r\n"
-        ]
-    ),
-    Module:send(Socket, Chunk),
-    Module:send(Socket, Chunk),
-    Module:send(Socket, Chunk),
-    Module:send(Socket, "0\r\n\r\n").
-
-slow_chunked_response(Module, Socket, _, _, _) ->
-    ChunkSize = erlang:integer_to_list(length(?LONG_BODY_PART) * 2, 16),
-    Module:send(
-        Socket,
-        [
-            "HTTP/1.1 200 OK\r\n"
-            "Content-type: text/plain\r\n"
-            "Transfer-Encoding: chunked\r\n\r\n"
-        ]),
-    Module:send(Socket, [ChunkSize, "\r\n", <>]),
-    timer:sleep(200),
-    Module:send(Socket, [<>, "\r\n"]),
-    Module:send(Socket, "0\r\n\r\n").
-
-
-chunked_upload(Module, Socket, _, Headers, <<>>) ->
-    TransferEncoding = lhttpc_lib:header_value("transfer-encoding", Headers),
-    {Body, HeadersAndTrailers} =
-        webserver:read_chunked(Module, Socket, Headers),
-    Trailer1 = lhttpc_lib:header_value("x-trailer-1", HeadersAndTrailers,
-        "undefined"),
-    Module:send(
-        Socket,
-        [
-            "HTTP/1.1 200 OK\r\n"
-            "Content-Length: 14\r\n"
-            "X-Test-Orig-Trailer-1:", Trailer1, "\r\n"
-            "X-Test-Orig-Enc: ", TransferEncoding, "\r\n"
-            "X-Test-Orig-Body: ", Body, "\r\n\r\n"
-            ?DEFAULT_STRING
-        ]
-    ).
-
-head_response(Module, Socket, _Request, _Headers, _Body) ->
-    Module:send(
-        Socket,
-        "HTTP/1.1 200 OK\r\n"
-        "Server: Test server!\r\n\r\n"
-    ).
-
-no_content_response(Module, Socket, _Request, _Headers, _Body) ->
-    Module:send(
-        Socket,
-        "HTTP/1.1 204 OK\r\n"
-        "Server: Test server!\r\n\r\n"
-    ).
-
-empty_body(Module, Socket, _, _, _) ->
-    Module:send(
-        Socket,
-        "HTTP/1.1 200 OK\r\n"
-        "Content-type: text/plain\r\nContent-length: 0\r\n\r\n"
-    ).
-
-copy_body(Module, Socket, _, _, Body) ->
-    Module:send(
-        Socket,
-        [
-            "HTTP/1.1 200 OK\r\n"
-            "Content-type: text/plain\r\nContent-length: "
-            ++ integer_to_list(size(Body)) ++ "\r\n\r\n",
-            Body
-        ]
-    ).
-
-copy_body_100_continue(Module, Socket, _, _, Body) ->
-    Module:send(
-        Socket,
-        [
-            "HTTP/1.1 100 Continue\r\n\r\n"
-            "HTTP/1.1 200 OK\r\n"
-            "Content-type: text/plain\r\nContent-length: "
-            ++ integer_to_list(size(Body)) ++ "\r\n\r\n",
-            Body
-        ]
-    ).
-
-respond_and_close(Module, Socket, _, _, Body) ->
-    Pid = list_to_pid(binary_to_list(Body)),
-    Module:send(
-        Socket,
-        "HTTP/1.1 200 OK\r\n"
-        "Connection: close\r\n"
-        "Content-type: text/plain\r\nContent-length: 14\r\n\r\n"
-        ?DEFAULT_STRING
-    ),
-    {error, closed} = Module:recv(Socket, 0),
-    Pid ! closed,
-    Module:close(Socket).
-
-respond_and_wait(Module, Socket, _, _, Body) ->
-    Pid = list_to_pid(binary_to_list(Body)),
-    Module:send(
-        Socket,
-        "HTTP/1.1 200 OK\r\n"
-        "Content-type: text/plain\r\nContent-length: 14\r\n\r\n"
-        ?DEFAULT_STRING
-    ),
-    % We didn't signal a connection close, but we want the client to do that
-    % any way
-    {error, closed} = Module:recv(Socket, 0),
-    Pid ! closed,
-    Module:close(Socket).
-
-pre_1_1_server(Module, Socket, _, _, Body) ->
-    Pid = list_to_pid(binary_to_list(Body)),
-    Module:send(
-        Socket,
-        "HTTP/1.0 200 OK\r\n"
-        "Content-type: text/plain\r\nContent-length: 14\r\n\r\n"
-        ?DEFAULT_STRING
-    ),
-    % We didn't signal a connection close, but we want the client to do that
-    % any way since we're 1.0 now
-    {error, closed} = Module:recv(Socket, 0),
-    Pid ! closed,
-    Module:close(Socket).
-
-pre_1_1_server_keep_alive(Module, Socket, _, _, _) ->
-    Module:send(
-        Socket,
-        "HTTP/1.0 200 OK\r\n"
-        "Content-type: text/plain\r\n"
-        "Connection: Keep-Alive\r\n"
-        "Content-length: 14\r\n\r\n"
-        ?DEFAULT_STRING
-    ).
-
-very_slow_response(Module, Socket, _, _, _) ->
-    timer:sleep(1000),
-    Module:send(
-        Socket,
-        "HTTP/1.1 200 OK\r\n"
-        "Content-type: text/plain\r\nContent-length: 14\r\n\r\n"
-        ?DEFAULT_STRING
-    ).
-
-no_content_length(Module, Socket, _, _, _) ->
-    Module:send(
-        Socket,
-        "HTTP/1.1 200 OK\r\n"
-        "Content-type: text/plain\r\nConnection: close\r\n\r\n"
-        ?DEFAULT_STRING
-    ).
-
-no_content_length_1_0(Module, Socket, _, _, _) ->
-    Module:send(
-        Socket,
-        "HTTP/1.0 200 OK\r\n"
-        "Content-type: text/plain\r\n\r\n"
-        ?DEFAULT_STRING
-    ).
-
-chunked_response(Module, Socket, _, _, _) ->
-    Module:send(
-        Socket,
-        "HTTP/1.1 200 OK\r\n"
-        "Content-type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n"
-        "5\r\n"
-        "Great\r\n"
-        "1\r\n"
-        " \r\n"
-        "8\r\n"
-        "success!\r\n"
-        "0\r\n"
-        "\r\n"
-    ).
-
-chunked_response_t(Module, Socket, _, _, _) ->
-    Module:send(
-        Socket,
-        "HTTP/1.1 200 OK\r\n"
-        "Content-type: text/plain\r\nTransfer-Encoding: ChUnKeD\r\n\r\n"
-        "7\r\n"
-        "Again, \r\n"
-        "E\r\n"
-        "great success!\r\n"
-        "0\r\n"
-        "Trailer-1: 1\r\n"
-        "Trailer-2: 2\r\n"
-        "\r\n"
-    ).
-
-close_connection(Module, Socket, _, _, _) ->
-    Module:send(
-        Socket,
-        "HTTP/1.1 200 OK\r\n"
-        "Content-type: text/plain\r\nContent-length: 14\r\n\r\n"
-    ),
-    Module:close(Socket).
-
-not_modified_response(Module, Socket, _Request, _Headers, _Body) ->
-    Module:send(
-        Socket,
-		[
-			"HTTP/1.1 304 Not Modified\r\n"
-			"Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n\r\n"
-		]
-    ).
-
-basic_auth_responder(User, Passwd) ->
-    fun(Module, Socket, _Request, Headers, _Body) ->
-        case proplists:get_value("Authorization", Headers) of
-            undefined ->
-                Module:send(
-                    Socket,
-                    [
-                        "HTTP/1.1 401 Unauthorized\r\n",
-                        "Content-Type: text/plain\r\n",
-                        "Content-Length: 12\r\n\r\n",
-                        "missing_auth"
-                    ]
-                );
-            "Basic " ++ Auth ->
-                [U, P] = string:tokens(
-                    binary_to_list(base64:decode(iolist_to_binary(Auth))), ":"),
-                case {U, P} of
-                    {User, Passwd} ->
-                        Module:send(
-                            Socket,
-                            [
-                                "HTTP/1.1 200 OK\r\n",
-                                "Content-Type: text/plain\r\n",
-                                "Content-Length: 2\r\n\r\n",
-                                "OK"
-                            ]
-                        );
-                    _ ->
-                        Module:send(
-                            Socket,
-                            [
-                                "HTTP/1.1 401 Unauthorized\r\n",
-                                "Content-Type: text/plain\r\n",
-                                "Content-Length: 10\r\n\r\n",
-                                "wrong_auth"
-                            ]
-                        )
-                end
-        end
-    end.
-
-trailing_space_header(Module, Socket, _, _, _) ->
-    Module:send(
-      Socket,
-      "HTTP/1.1 200 OK\r\n"
-          "Content-type: text/plain\r\n"
-          "Content-Length: 14 \r\n\r\n"
-          ?DEFAULT_STRING
-    ).
diff --git a/test/simple_load.erl b/test/simple_load.erl
index 0e49b273..2a29f84d 100644
--- a/test/simple_load.erl
+++ b/test/simple_load.erl
@@ -1,5 +1,4 @@
 -module(simple_load).
--behaviour(gen_httpd).
 
 -export([start_client/2, start_client/3]).
 -export([client/2]).
@@ -9,75 +8,75 @@
 
 %%% Client part
 start_client(Port, Clients) ->
-	start_client("localhost", Port, Clients).
+    start_client("localhost", Port, Clients).
 
 start_client(Host, Port, Clients) when Clients > 0 ->
-	start_applications([crypto, ssl, lhttpc]),
-	process_flag(trap_exit, true),
-	{ok, Body} = file:read_file("test/1M"),
-	URL = "http://" ++ Host ++ ":" ++ integer_to_list(Port) ++ "/static/1M",
-	start(Clients, URL, Body, Clients).
+    start_applications([crypto, ssl, lhttpc]),
+    process_flag(trap_exit, true),
+    {ok, Body} = file:read_file("test/1M"),
+    URL = "http://" ++ Host ++ ":" ++ integer_to_list(Port) ++ "/static/1M",
+    start(Clients, URL, Body, Clients).
 
 start(0, _, _, No) ->
-	wait_exit(No, []);
+    wait_exit(No, []);
 start(Clients, URL, Body, No) ->
-	spawn_link(?MODULE, client, [URL, Body]),
-	start(Clients - 1, URL, Body, No).
+    spawn_link(?MODULE, client, [URL, Body]),
+    start(Clients - 1, URL, Body, No).
 
 wait_exit(0, []) ->
-	ok;
+    ok;
 wait_exit(0, Errors) ->
-	{error, Errors};
+    {error, Errors};
 wait_exit(No, Errors) ->
-	receive
-		{'EXIT', _, normal} ->
-			wait_exit(No - 1, Errors);
-		{'EXIT', _, Reason} ->
-			wait_exit(No - 1, [Reason | Errors])
-	end.
+    receive
+        {'EXIT', _, normal} ->
+            wait_exit(No - 1, Errors);
+        {'EXIT', _, Reason} ->
+            wait_exit(No - 1, [Reason | Errors])
+    end.
 
 client(URL, Body) ->
-	case lhttpc:request(URL, "POST", [], Body, 60000) of
-		{ok, {{200, _}, _, Body}} ->
-			ok;
-		Other ->
-			exit({bad_result, Other})
-	end.
+    case lhttpc:request(URL, "POST", [], Body, 60000) of
+        {ok, {{200, _}, _, Body}} ->
+            ok;
+        Other ->
+            exit({bad_result, Other})
+    end.
 
 %%% Server part
 start_server() ->
-	SockOpts = [{backlog, 10000}],
-	{ok, Pid} = gen_httpd:start_link(?MODULE, nil, 0, 600000, SockOpts),
-	gen_httpd:port(Pid).
+    SockOpts = [{backlog, 10000}],
+    {ok, Pid} = gen_httpd:start_link(?MODULE, nil, 0, 600000, SockOpts),
+    gen_httpd:port(Pid).
 
 init(_, _) ->
-	{ok, nil}.
+    {ok, nil}.
 
 handle_continue(_Method, _URI, _Vsn, _ReqHdrs, CBState) ->
-	{continue, [], CBState}.
+    {continue, [], CBState}.
 
 handle_request(_Method, "/static/1M", {1,1}, _, EntityBody, State) ->
-	case EntityBody of
-		{identity, EntityState} ->
-			case gen_httpd:read_body(complete, 50000, EntityState) of
-				{ok, {Body, http_eob}} ->
-					{reply, 200, [], Body, State};
-				{error, Reason} ->
-					{reply, 500, [], io_lib:format("~p", [Reason]), State}
-			end;
-		_ ->
-			{reply, 406, [], <<"No request body">>, State}
-	end.
+    case EntityBody of
+        {identity, EntityState} ->
+            case gen_httpd:read_body(complete, 50000, EntityState) of
+                {ok, {Body, http_eob}} ->
+                    {reply, 200, [], Body, State};
+                {error, Reason} ->
+                    {reply, 500, [], io_lib:format("~p", [Reason]), State}
+            end;
+        _ ->
+            {reply, 406, [], <<"No request body">>, State}
+    end.
 
 terminate(_, _) ->
-	ok.
+    ok.
 
 start_applications(Apps) ->
-	Started = lists:map(fun({Name, _, _}) -> Name end,
-		application:which_applications()),
-	lists:foreach(fun(App) ->
-				case lists:member(App, Started) of
-					false -> ok = application:start(App);
-					true  -> ok
-				end
-		end, Apps).
+    Started = lists:map(fun({Name, _, _}) -> Name end,
+        application:which_applications()),
+    lists:foreach(fun(App) ->
+                case lists:member(App, Started) of
+                    false -> ok = application:start(App);
+                    true  -> ok
+                end
+        end, Apps).
diff --git a/test/socket_server.erl b/test/socket_server.erl
index 15fdcd83..412b8c76 100644
--- a/test/socket_server.erl
+++ b/test/socket_server.erl
@@ -1,7 +1,7 @@
 %%% ----------------------------------------------------------------------------
-%%% Copyright (c) 2009, Erlang Training and Consulting Ltd.
+%%% Copyright (c) 2009-2013, Erlang Solutions Ltd.
 %%% All rights reserved.
-%%% 
+%%%
 %%% Redistribution and use in source and binary forms, with or without
 %%% modification, are permitted provided that the following conditions are met:
 %%%    * Redistributions of source code must retain the above copyright
@@ -9,14 +9,14 @@
 %%%    * Redistributions in binary form must reproduce the above copyright
 %%%      notice, this list of conditions and the following disclaimer in the
 %%%      documentation and/or other materials provided with the distribution.
-%%%    * Neither the name of Erlang Training and Consulting Ltd. nor the
+%%%    * Neither the name of Erlang Solutions Ltd. nor the
 %%%      names of its contributors may be used to endorse or promote products
 %%%      derived from this software without specific prior written permission.
-%%% 
-%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS''
+%%%
+%%% THIS SOFTWARE IS PROVIDED BY Erlang Solutions Ltd. ''AS IS''
 %%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 %%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE
+%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Solutions Ltd. BE
 %%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 %%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 %%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
diff --git a/test/webserver.erl b/test/webserver.erl
index 7da8cc63..b72d891c 100644
--- a/test/webserver.erl
+++ b/test/webserver.erl
@@ -1,7 +1,7 @@
 %%% ----------------------------------------------------------------------------
-%%% Copyright (c) 2009, Erlang Training and Consulting Ltd.
+%%% Copyright (c) 2009-2013, Erlang Solutions Ltd.
 %%% All rights reserved.
-%%% 
+%%%
 %%% Redistribution and use in source and binary forms, with or without
 %%% modification, are permitted provided that the following conditions are met:
 %%%    * Redistributions of source code must retain the above copyright
@@ -9,14 +9,14 @@
 %%%    * Redistributions in binary form must reproduce the above copyright
 %%%      notice, this list of conditions and the following disclaimer in the
 %%%      documentation and/or other materials provided with the distribution.
-%%%    * Neither the name of Erlang Training and Consulting Ltd. nor the
+%%%    * Neither the name of Erlang Solutionsg Ltd. nor the
 %%%      names of its contributors may be used to endorse or promote products
 %%%      derived from this software without specific prior written permission.
-%%% 
-%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS''
+%%%
+%%% THIS SOFTWARE IS PROVIDED BY Erlang Solutions Ltd. ''AS IS''
 %%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 %%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE
+%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Solutions Ltd. BE
 %%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 %%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 %%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
diff --git a/test/webserver_utils.erl b/test/webserver_utils.erl
new file mode 100644
index 00000000..474e9c6c
--- /dev/null
+++ b/test/webserver_utils.erl
@@ -0,0 +1,430 @@
+-module(webserver_utils).
+
+-compile(export_all).
+
+-define(DEFAULT_STRING, "Great success!").
+-define(LONG_BODY_PART,
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+        "This is a relatively long body, that we send to the client... "
+    ).
+
+default_string() ->
+    ?DEFAULT_STRING.
+
+long_body_part() ->
+    ?LONG_BODY_PART.
+
+long_body_part(Size) ->
+    list_to_binary(
+      lists:foldl(
+    fun(_, Acc) ->
+        Acc ++ " " ++ webserver_utils:long_body_part()
+    end, webserver_utils:long_body_part(), lists:seq(1, Size))).
+
+%%% Responders
+simple_response(Module, Socket, _Request, _Headers, Body) ->
+    Module:send(
+        Socket,
+        [
+            "HTTP/1.1 200 OK\r\n"
+            "Content-type: text/plain\r\nContent-length: 14\r\n"
+            "X-Test-Orig-Body: ", Body, "\r\n\r\n"
+            ?DEFAULT_STRING
+        ]
+    ).
+
+large_response(Module, Socket, _, _, _) ->
+    BodyPart = <>,
+    ContentLength = 3 * size(BodyPart),
+    Module:send(
+        Socket,
+        [
+            "HTTP/1.1 200 OK\r\n"
+            "Content-type: text/plain\r\n"
+            "Content-length: ", integer_to_list(ContentLength), "\r\n\r\n"
+        ]
+    ),
+    Module:send(Socket, BodyPart),
+    Module:send(Socket, BodyPart),
+    Module:send(Socket, BodyPart).
+
+large_chunked_response(Module, Socket, _, _, _) ->
+    BodyPart = <>,
+    ChunkSize = erlang:integer_to_list(size(BodyPart), 16),
+    Chunk = [ChunkSize, "\r\n", BodyPart, "\r\n"],
+    Module:send(
+        Socket,
+        [
+            "HTTP/1.1 200 OK\r\n"
+            "Content-type: text/plain\r\n"
+            "Transfer-Encoding: chunked\r\n\r\n"
+        ]
+    ),
+    Module:send(Socket, Chunk),
+    Module:send(Socket, Chunk),
+    Module:send(Socket, Chunk),
+    Module:send(Socket, "0\r\n\r\n").
+
+slow_chunked_response(Module, Socket, _, _, _) ->
+    ChunkSize = erlang:integer_to_list(length(?LONG_BODY_PART) * 2, 16),
+    Module:send(
+        Socket,
+        [
+            "HTTP/1.1 200 OK\r\n"
+            "Content-type: text/plain\r\n"
+            "Transfer-Encoding: chunked\r\n\r\n"
+        ]),
+    Module:send(Socket, [ChunkSize, "\r\n", <>]),
+    timer:sleep(200),
+    Module:send(Socket, [<>, "\r\n"]),
+    Module:send(Socket, "0\r\n\r\n").
+
+
+chunked_upload(Module, Socket, _, Headers, <<>>) ->
+    TransferEncoding = lhttpc_lib:header_value("transfer-encoding", Headers),
+    {Body, HeadersAndTrailers} =
+        webserver:read_chunked(Module, Socket, Headers),
+    Trailer1 = lhttpc_lib:header_value("x-trailer-1", HeadersAndTrailers,
+        "undefined"),
+    Module:send(
+        Socket,
+        [
+            "HTTP/1.1 200 OK\r\n"
+            "Content-Length: 14\r\n"
+            "X-Test-Orig-Trailer-1:", Trailer1, "\r\n"
+            "X-Test-Orig-Enc: ", TransferEncoding, "\r\n"
+            "X-Test-Orig-Body: ", Body, "\r\n\r\n"
+            ?DEFAULT_STRING
+        ]
+    ).
+
+head_response(Module, Socket, _Request, _Headers, _Body) ->
+    Module:send(
+        Socket,
+        "HTTP/1.1 200 OK\r\n"
+        "Server: Test server!\r\n\r\n"
+    ).
+
+no_content_response(Module, Socket, _Request, _Headers, _Body) ->
+    Module:send(
+        Socket,
+        "HTTP/1.1 204 OK\r\n"
+        "Server: Test server!\r\n\r\n"
+    ).
+
+empty_body(Module, Socket, _, _, _) ->
+    Module:send(
+        Socket,
+        "HTTP/1.1 200 OK\r\n"
+        "Content-type: text/plain\r\nContent-length: 0\r\n\r\n"
+    ).
+
+copy_body(Module, Socket, _, _, Body) ->
+    Module:send(
+        Socket,
+        [
+            "HTTP/1.1 200 OK\r\n"
+            "Content-type: text/plain\r\nContent-length: "
+            ++ integer_to_list(size(Body)) ++ "\r\n\r\n",
+            Body
+        ]
+    ).
+
+copy_body_100_continue(Module, Socket, _, _, Body) ->
+    Module:send(
+        Socket,
+        [
+            "HTTP/1.1 100 Continue\r\n\r\n"
+            "HTTP/1.1 200 OK\r\n"
+            "Content-type: text/plain\r\nContent-length: "
+            ++ integer_to_list(size(Body)) ++ "\r\n\r\n",
+            Body
+        ]
+    ).
+
+respond_and_close(Module, Socket, _, _, Body) ->
+    Pid = list_to_pid(binary_to_list(Body)),
+    Module:send(
+        Socket,
+        "HTTP/1.1 200 OK\r\n"
+        "Connection: close\r\n"
+        "Content-type: text/plain\r\nContent-length: 14\r\n\r\n"
+        ?DEFAULT_STRING
+    ),
+    {error, closed} = Module:recv(Socket, 0),
+    Pid ! closed,
+    Module:close(Socket).
+
+respond_and_wait(Module, Socket, _, _, Body) ->
+    Pid = list_to_pid(binary_to_list(Body)),
+    Module:send(
+        Socket,
+        "HTTP/1.1 200 OK\r\n"
+        "Content-type: text/plain\r\nContent-length: 14\r\n\r\n"
+        ?DEFAULT_STRING
+    ),
+    % We didn't signal a connection close, but we want the client to do that
+    % any way
+    {error, closed} = Module:recv(Socket, 0),
+    Pid ! closed,
+    Module:close(Socket).
+
+pre_1_1_server(Module, Socket, _, _, Body) ->
+    Pid = list_to_pid(binary_to_list(Body)),
+    Module:send(
+        Socket,
+        "HTTP/1.0 200 OK\r\n"
+        "Content-type: text/plain\r\nContent-length: 14\r\n\r\n"
+        ?DEFAULT_STRING
+    ),
+    % We didn't signal a connection close, but we want the client to do that
+    % any way since we're 1.0 now
+    {error, closed} = Module:recv(Socket, 0),
+    Pid ! closed,
+    Module:close(Socket).
+
+pre_1_1_server_keep_alive(Module, Socket, _, _, _) ->
+    Module:send(
+        Socket,
+        "HTTP/1.0 200 OK\r\n"
+        "Content-type: text/plain\r\n"
+        "Connection: Keep-Alive\r\n"
+        "Content-length: 14\r\n\r\n"
+        ?DEFAULT_STRING
+    ).
+
+very_slow_response(Module, Socket, _, _, _) ->
+    timer:sleep(1000),
+    Module:send(
+        Socket,
+        "HTTP/1.1 200 OK\r\n"
+        "Content-type: text/plain\r\nContent-length: 14\r\n\r\n"
+        ?DEFAULT_STRING
+    ).
+
+no_content_length(Module, Socket, _, _, _) ->
+    Module:send(
+        Socket,
+        "HTTP/1.1 200 OK\r\n"
+        "Content-type: text/plain\r\nConnection: close\r\n\r\n"
+        ?DEFAULT_STRING
+    ).
+
+no_content_length_1_0(Module, Socket, _, _, _) ->
+    Module:send(
+        Socket,
+        "HTTP/1.0 200 OK\r\n"
+        "Content-type: text/plain\r\n\r\n"
+        ?DEFAULT_STRING
+    ).
+
+chunked_response(Module, Socket, _, _, _) ->
+    Module:send(
+        Socket,
+        "HTTP/1.1 200 OK\r\n"
+        "Content-type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n"
+        "5\r\n"
+        "Great\r\n"
+        "1\r\n"
+        " \r\n"
+        "8\r\n"
+        "success!\r\n"
+        "0\r\n"
+        "\r\n"
+    ).
+
+chunked_response_t(Module, Socket, _, _, _) ->
+    Module:send(
+        Socket,
+        "HTTP/1.1 200 OK\r\n"
+        "Content-type: text/plain\r\nTransfer-Encoding: ChUnKeD\r\n\r\n"
+        "7\r\n"
+        "Again, \r\n"
+        "E\r\n"
+        "great success!\r\n"
+        "0\r\n"
+        "Trailer-1: 1\r\n"
+        "Trailer-2: 2\r\n"
+        "\r\n"
+    ).
+
+close_connection(Module, Socket, _, _, _) ->
+    Module:send(
+        Socket,
+        "HTTP/1.1 200 OK\r\n"
+        "Content-type: text/plain\r\nContent-length: 14\r\n\r\n"
+    ),
+    Module:close(Socket).
+
+not_modified_response(Module, Socket, _Request, _Headers, _Body) ->
+    Module:send(
+        Socket,
+        [
+            "HTTP/1.1 304 Not Modified\r\n"
+            "Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n\r\n"
+        ]
+    ).
+
+basic_auth_responder(User, Passwd) ->
+    fun(Module, Socket, _Request, Headers, _Body) ->
+        case proplists:get_value("Authorization", Headers) of
+            undefined ->
+                Module:send(
+                    Socket,
+                    [
+                        "HTTP/1.1 401 Unauthorized\r\n",
+                        "Content-Type: text/plain\r\n",
+                        "Content-Length: 12\r\n\r\n",
+                        "missing_auth"
+                    ]
+                );
+            "Basic " ++ Auth ->
+                [U, P] = string:tokens(
+                    binary_to_list(base64:decode(iolist_to_binary(Auth))), ":"),
+                case {U, P} of
+                    {User, Passwd} ->
+                        Module:send(
+                            Socket,
+                            [
+                                "HTTP/1.1 200 OK\r\n",
+                                "Content-Type: text/plain\r\n",
+                                "Content-Length: 2\r\n\r\n",
+                                "OK"
+                            ]
+                        );
+                    _ ->
+                        Module:send(
+                            Socket,
+                            [
+                                "HTTP/1.1 401 Unauthorized\r\n",
+                                "Content-Type: text/plain\r\n",
+                                "Content-Length: 10\r\n\r\n",
+                                "wrong_auth"
+                            ]
+                        )
+                end
+        end
+    end.
+
+trailing_space_header(Module, Socket, _, _, _) ->
+    Module:send(
+      Socket,
+      "HTTP/1.1 200 OK\r\n"
+          "Content-type: text/plain\r\n"
+          "Content-Length: 14 \r\n\r\n"
+          ?DEFAULT_STRING
+    ).
+
+set_cookie_response(Module, Socket, _, _, _) ->
+     Module:send(
+      Socket,
+       "HTTP/1.1 200 OK\r\n"
+       "Connection: Keep-Alive\r\n"
+       "Set-Cookie: name=value\r\n"
+       "Set-Cookie: name2=value2; Expires=Wed, 09-Jun-2021 10:18:14 GMT\r\n"
+       "Content-type: text/plain\r\n"
+       "Content-length: 0\r\n\r\n"
+      ).
+
+
+expired_cookie_response(Module, Socket, _Request, Headers, _Body) ->
+    case lhttpc_lib:header_value("Cookie", Headers) of
+            undefined ->
+                Module:send(
+                    Socket,
+             "HTTP/1.1 500 Internal Server Error\r\n"
+             "Content-type: text/plain\r\n"
+             "Content-length: 0\r\n\r\n"
+                );
+    "name=value; name2=value2" ->
+        Module:send(
+          Socket,
+          "HTTP/1.1 200 OK\r\n"
+          "Connection: Keep-Alive\r\n"
+          "Set-Cookie: name2=value2; Expires=Wed, 09-Jun-1975 10:18:14 GMT\r\n"
+          "Content-type: text/plain\r\n"
+              "Content-length: 0\r\n\r\n"
+         );
+    %The order should not matter.
+    "name2=value2; name=value"->
+        Module:send(
+          Socket,
+          "HTTP/1.1 200 OK\r\n"
+          "Connection: Keep-Alive\r\n"
+          "Set-Cookie: name2=value2; Expires=Wed, 09-Jun-1975 10:18:14 GMT\r\n"
+          "Content-type: text/plain\r\n"
+              "Content-length: 0\r\n\r\n"
+         )
+    end.
+
+receive_right_cookies(Module, Socket, _Request, Headers, _Body) ->
+    case proplists:get_value("Cookie", Headers) of
+    "name=value" ->
+        Module:send(
+          Socket,
+          "HTTP/1.1 200 OK\r\n"
+          "Content-type: text/plain\r\n"
+          "Content-length: 0\r\n\r\n"
+         );
+    _ ->
+         Module:send(
+                    Socket,
+             "HTTP/1.1 500 Internal Server Error\r\n"
+             "Content-type: text/plain\r\n"
+             "Content-length: 0\r\n\r\n"
+                )
+    end.
diff --git a/util/make_doc.erl b/util/make_doc.erl
deleted file mode 100644
index 53beba11..00000000
--- a/util/make_doc.erl
+++ /dev/null
@@ -1,37 +0,0 @@
-%%% ----------------------------------------------------------------------------
-%%% Copyright (c) 2009, Erlang Training and Consulting Ltd.
-%%% All rights reserved.
-%%% 
-%%% Redistribution and use in source and binary forms, with or without
-%%% modification, are permitted provided that the following conditions are met:
-%%%    * Redistributions of source code must retain the above copyright
-%%%      notice, this list of conditions and the following disclaimer.
-%%%    * Redistributions in binary form must reproduce the above copyright
-%%%      notice, this list of conditions and the following disclaimer in the
-%%%      documentation and/or other materials provided with the distribution.
-%%%    * Neither the name of Erlang Training and Consulting Ltd. nor the
-%%%      names of its contributors may be used to endorse or promote products
-%%%      derived from this software without specific prior written permission.
-%%% 
-%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS''
-%%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-%%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE
-%%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-%%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-%%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-%%% OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-%%% ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-%%% ----------------------------------------------------------------------------
-
-%%% @author Oscar Hellström 
--module(make_doc).
--export([edoc/0]).
-
-edoc() ->
-    try
-        edoc:application(lhttpc, "./", [{doc, "doc/"}])
-    catch _:_ ->
-        halt(1)
-    end,
-    halt(0).
diff --git a/util/releaser b/util/releaser
deleted file mode 100755
index 8d4400c0..00000000
--- a/util/releaser
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/bin/sh
-# ----------------------------------------------------------------------------
-# Copyright (c) 2009, Erlang Training and Consulting Ltd.
-# All rights reserved.
-# 
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#    * Redistributions of source code must retain the above copyright
-#      notice, this list of conditions and the following disclaimer.
-#    * Redistributions in binary form must reproduce the above copyright
-#      notice, this list of conditions and the following disclaimer in the
-#      documentation and/or other materials provided with the distribution.
-#    * Neither the name of Erlang Training and Consulting Ltd. nor the
-#      names of its contributors may be used to endorse or promote products
-#      derived from this software without specific prior written permission.
-# 
-# THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS''
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE
-# LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-# ----------------------------------------------------------------------------
-# Script for making a release of lhttpc. Or any program in hg really. Check
-# the Makefile target release for how to use it.
-
-NAME=$1
-VSN=$2
-
-yesno() {
-    prompt=$1
-    while true; do
-        read -p "$1 [Y/n] " answer
-        case "x$answer" in
-            "x")
-            return 0
-            ;;
-            "xY")
-            return 0
-            ;;
-            "xy")
-            return 0
-            ;;
-            "xN")
-            return 1
-            ;;
-            "xn")
-            return 1
-            ;;
-            *)
-            ;;
-        esac
-    done
-}
-
-get_version() {
-    while true; do
-        read -p "What is the version of the release? [$VSN] " release_vsn
-
-        if [ "$release_vsn" = "" ]; then
-            release_vsn=$VSN
-        fi
-
-        if $(echo "$TAGS" | grep -q "^$release_vsn\$"); then
-            if yesno "A tag exists for version $release_vsn, is this correct?"; then
-                break
-            fi
-        else
-            if yesno "A tag doesn't exist for version $release_vsn, should one be created?"; then
-                hg tag $release_vsn
-            fi
-            break
-        fi
-    done
-    echo $release_vsn
-}
-
-if ! hg identify 1>/dev/null 2>&1; then
-    echo "No hg repository here..."
-    exit 1
-fi
-
-if ! [ "$(hg identify | awk '{print $2};')" = "tip" ]; then
-    if ! yesno "Repository is not at tip, do you want to continue?"; then
-        exit 1
-    fi
-fi
-
-if ! yesno "Did the compilation run without warnings?"; then
-    echo "Try again..."
-    exit 1
-fi
-
-if ! yesno "Is the changelog up to date?"; then
-    echo "Try again..."
-    exit 1
-fi
-
-if ! yesno "Did dialyzer run without warnings?"; then
-    echo "Try again..."
-    exit 1
-fi
-
-TAGS=$(hg tags | awk '{print $1 };' | grep -v "^tip$")
-LATEST_TAG=$(echo "$TAGS" | head -n 1)
-
-RELEASE_VSN=$(get_version)
-echo "Creating a release for $NAME-$RELEASE_VSN now."
-archive="./$NAME-$RELEASE_VSN.tar.gz"
-if [ -e $archive ]; then
-    echo "$archive exists, giving up."
-    exit 1
-fi
-hg archive -t tgz -X ".hg*" $archive
diff --git a/util/run_test.erl b/util/run_test.erl
deleted file mode 100644
index b759e664..00000000
--- a/util/run_test.erl
+++ /dev/null
@@ -1,122 +0,0 @@
-%%% ----------------------------------------------------------------------------
-%%% Copyright (c) 2009, Erlang Training and Consulting Ltd.
-%%% All rights reserved.
-%%% 
-%%% Redistribution and use in source and binary forms, with or without
-%%% modification, are permitted provided that the following conditions are met:
-%%%    * Redistributions of source code must retain the above copyright
-%%%      notice, this list of conditions and the following disclaimer.
-%%%    * Redistributions in binary form must reproduce the above copyright
-%%%      notice, this list of conditions and the following disclaimer in the
-%%%      documentation and/or other materials provided with the distribution.
-%%%    * Neither the name of Erlang Training and Consulting Ltd. nor the
-%%%      names of its contributors may be used to endorse or promote products
-%%%      derived from this software without specific prior written permission.
-%%% 
-%%% THIS SOFTWARE IS PROVIDED BY Erlang Training and Consulting Ltd. ''AS IS''
-%%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-%%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-%%% ARE DISCLAIMED. IN NO EVENT SHALL Erlang Training and Consulting Ltd. BE
-%%% LIABLE SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-%%% BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-%%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-%%% OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-%%% ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-%%% ----------------------------------------------------------------------------
-
-%%% @author Oscar Hellström 
--module(run_test).
--export([run/0]).
-
--include_lib("eunit/include/eunit.hrl").
--include_lib("stdlib/include/ms_transform.hrl").
-
--define(TEST_LOG, "test/error_logger.log").
--define(SASL_LOG, "test/sasl.log").
--define(FILE_NAME(MODULE),
-    "cover_report/" ++ atom_to_list(MODULE) ++ ".html").
-
-run() ->
-    Modules = get_modules(),
-    ok = cover_compile(Modules),
-    start_logging(),
-    Result = eunit:test(?MODULE, [verbose]),
-    filelib:ensure_dir("cover_report/index.html"),
-    html_report(Modules),
-    write_report(Modules),
-    stop_logging(),
-    io:format("Cover report in cover_report/index.html~n"),
-    io:format("Test logs in ~s and ~s~n", [?TEST_LOG, ?SASL_LOG]),
-    if
-        Result =:= ok -> halt(0);
-        Result =/= ok -> halt(1)
-    end.
-
-start_logging() ->
-    application:load(sasl),
-    application:set_env(sasl, sasl_error_logger, {file, ?SASL_LOG}),
-    file:delete(?TEST_LOG),
-    file:delete(?SASL_LOG),
-    error_logger:tty(false),
-    error_logger:logfile({open, ?TEST_LOG}),
-    application:start(sasl).
-
-stop_logging() ->
-    error_logger:logfile(close),
-    application:stop(sasl).
-
-html_report([Module | Modules]) ->
-    cover:analyse_to_file(Module, ?FILE_NAME(Module), [html]),
-    html_report(Modules);
-html_report([]) ->
-    ok.
-
-write_report(Modules) ->
-    {TotalPercentage, ModulesPersentage} = percentage(Modules, 0, 0, []),
-    file:write_file("cover_report/index.html",
-        [
-            "\nCover report index\n"
-            "\n"
-            "

Cover report for lhttpc

" - "Total coverage: ", integer_to_list(TotalPercentage), "%" - "

Cover for individual modules

\n" - "
    \n\t", - lists:foldl(fun({Module, Percentage}, Acc) -> - Name = atom_to_list(Module), - [ - "
  • " - "", - Name, - " ", integer_to_list(Percentage), "%" - "
  • \n\t" | - Acc - ] - end, [], ModulesPersentage), - "
" - ]). - -percentage([Module | Modules], TotCovered, TotLines, Percentages) -> - {ok, Analasys} = cover:analyse(Module, coverage, line), - {Covered, Lines} = lists:foldl(fun({_, {C, _}}, {Covered, Lines}) -> - {C + Covered, Lines + 1} - end, {0, 0}, Analasys), - Percent = (Covered * 100) div Lines, - NewPercentages = [{Module, Percent} | Percentages], - percentage(Modules, Covered + TotCovered, Lines + TotLines, NewPercentages); -percentage([], Covered, Lines, Percentages) -> - {(Covered * 100) div Lines, Percentages}. - -get_modules() -> - application:load(lhttpc), - {ok, Modules} = application:get_key(lhttpc, modules), - Modules. - -cover_compile([Module | Modules]) -> - {ok, Module} = cover:compile_beam(Module), - cover_compile(Modules); -cover_compile([]) -> - ok. - -%%% Eunit functions -application_test_() -> - {application, lhttpc}.