Merge commit 'v2.2.0'

Conflicts:
	rebar
	src/ibrowse.app.src
	src/ibrowse_test.erl
diff --git a/.gitignore b/.gitignore
index 2a53be2..cb2494d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
-*.beam
-
+ebin/
+*~
+.eunit/
+test/
diff --git a/Makefile b/Makefile
index 2380676..4021c5b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,17 +1,19 @@
-include vsn.mk
+IBROWSE_VSN = $(shell sed -n 's/.*{vsn,.*"\(.*\)"}.*/\1/p' src/ibrowse.app.src)
 
 all:
-	(cd src ; make)
+	./rebar compile
 
 clean:
-	(cd src ; make clean)
+	./rebar clean
 
 install: all
 	mkdir -p $(DESTDIR)/lib/ibrowse-$(IBROWSE_VSN)/
 	cp -r ebin $(DESTDIR)/lib/ibrowse-$(IBROWSE_VSN)/
 
 test: all
-	erl -noshell -pa ebin -s ibrowse -s ibrowse_test unit_tests \
+	./rebar eunit
+	(cd test; make) 
+	erl -noshell -pa ebin -pa test -s ibrowse -s ibrowse_test unit_tests \
 	-s ibrowse_test verify_chunked_streaming \
 	-s ibrowse_test test_chunked_streaming_once \
 	-s erlang halt
diff --git a/README b/README
index d47f28d..b14822f 100644
--- a/README
+++ b/README
@@ -18,7 +18,7 @@
 
 Comments to : Chandrashekhar.Mullaparthi@gmail.com
 
-Version : 2.1.2
+Version : 2.2.0
 
 Latest version : git://github.com/cmullaparthi/ibrowse.git
 
@@ -43,6 +43,7 @@
 Geoff Cant
 Jeroen Koops
 João Lopes
+Joseph Wayne Norton
 Karol Skocik
 Kostis Sagonas
 Matthew Reilly
@@ -51,15 +52,43 @@
 Peter Kristensen
 Ram Krishnan
 Richard Cameron
+Ryan Zezeski
 Sean Hinde
 Seth Falcon
 Steve Vinoski
 Thomas Lindgren
 Younès Hafri
+fholzhauser (https://github.com/fholzhauser/)
 tholschuh (https://github.com/tholschuh/)
 
 CONTRIBUTIONS & CHANGE HISTORY
 ==============================
+13-04-2011 - v2.2.0
+             * Filipe David Manana added IPv6 support. This is a mjor new 
+               feature, Thank you Filipe!
+             * Joseph Wayne Norton contributed tweaks to .gitignore
+
+09-02-2011 - v2.1.4
+             * Fixed a bug reported by Ryan Zezeski with the
+               save_response_to_file option.
+               https://github.com/cmullaparthi/ibrowse/issues#issue/33
+
+16-01-2011 - v2.1.3
+             * Fixed issues with streaming and chunked responses when using
+               the 'caller controls socket' feature. See following links for
+               details. Contributed by Filipe David Manana.
+               https://github.com/cmullaparthi/ibrowse/pull/24
+               https://github.com/cmullaparthi/ibrowse/pull/25
+               https://github.com/cmullaparthi/ibrowse/pull/27
+               https://github.com/cmullaparthi/ibrowse/pull/28
+               https://github.com/cmullaparthi/ibrowse/pull/29
+
+             * Fix for issue 32 reported by fholzhauser
+               https://github.com/cmullaparthi/ibrowse/issues#issue/32
+
+             * Fixed some dialyzer warnings. Thanks to Kostis for reporting
+               them.
+
 20-12-2010 - v2.1.2
              * Pipelining wasn't working when used in conjunction with the
                {stream_to, {self(), once}} option. Bug report by
@@ -280,7 +309,7 @@
 12-01-2007 -  Derek Upham sent in a bug fix. The reset_state function was not
               behaving correctly when the transfer encoding was not chunked.
 
-13-11-2006 -  Youns Hafri reported a bug where ibrowse was not returning the 
+13-11-2006 -  Youns Hafri reported a bug where ibrowse was not returning the 
                 temporary filename when the server was closing the connection 
                 after sending the data (as in HTTP/1.0).
               Released ibrowse under the BSD license
@@ -299,7 +328,7 @@
 22-Nov-2005 - Added ability to generate requests using the Chunked 
               Transfer-Encoding.
 
-08-May-2005 - Youns Hafri made a CRUX LINUX port of ibrowse.
+08-May-2005 - Youns Hafri made a CRUX LINUX port of ibrowse.
               http://yhafri.club.fr/crux/index.html
 
 Here are some usage examples. Enjoy!
diff --git a/c_src/build_darwin b/c_src/build_darwin
deleted file mode 100755
index fbd041a..0000000
--- a/c_src/build_darwin
+++ /dev/null
@@ -1 +0,0 @@
-cc -o ../priv/ibrowse_drv.so -I ~/R9C-0/usr/include/ -bundle -flat_namespace -undefined suppress -fno-common ibrowse_drv.c
diff --git a/c_src/ibrowse_drv.c b/c_src/ibrowse_drv.c
deleted file mode 100644
index 8f912a9..0000000
--- a/c_src/ibrowse_drv.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/* Created 07/March/2004 Chandrashekhar Mullaparthi
-
-   $Id: ibrowse_drv.c,v 1.1 2005/05/05 22:28:28 chandrusf Exp $
-   
-   Erlang Linked in driver to URL encode a set of data
-*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "erl_driver.h"
-
-static ErlDrvData ibrowse_drv_start(ErlDrvPort port, char* buff);
-static void ibrowse_drv_stop(ErlDrvData handle);
-static void ibrowse_drv_command(ErlDrvData handle, char *buff, int bufflen);
-static void ibrowse_drv_finish(void);
-static int ibrowse_drv_control(ErlDrvData handle, unsigned int command, 
-                       char* buf, int count, char** res, int res_size);
-
-/* The driver entry */
-static ErlDrvEntry ibrowse_driver_entry = {
-    NULL,                          /* init, N/A */
-    ibrowse_drv_start,             /* start, called when port is opened */
-    ibrowse_drv_stop,              /* stop, called when port is closed */
-    NULL,                          /* output, called when erlang has sent */
-    NULL,                          /* ready_input, called when input descriptor 
-				      ready */
-    NULL,                          /* ready_output, called when output 
-				      descriptor ready */
-    "ibrowse_drv",                 /* char *driver_name, the argument 
-				      to open_port */
-    NULL,                          /* finish, called when unloaded */
-    NULL,                          /* void * that is not used (BC) */
-    ibrowse_drv_control,           /* control, port_control callback */
-    NULL,                          /* timeout, called on timeouts */
-    NULL,                          /* outputv, vector output interface */
-    NULL,
-    NULL,
-    NULL,                          /* call, synchronous call to driver */
-    NULL
-};
-
-typedef struct ibrowse_drv_data {
-  unsigned int count;
-  void *alloc_ptr;
-} State;
-
-static State *ibrowse_drv_data;
-
-DRIVER_INIT(ibrowse_drv)
-{
-  ibrowse_drv_data = NULL;
-  return &ibrowse_driver_entry;
-}
-
-static ErlDrvData ibrowse_drv_start(ErlDrvPort port, char *buff)
-{
-  State *state;
-
-  state = driver_alloc(sizeof(State));
-  state->count = 0;
-  state->alloc_ptr = NULL;
-
-  ibrowse_drv_data = state;
-  return ((ErlDrvData) state);
-}
-
-void ibrowse_drv_stop(ErlDrvData desc)
-{
-  return;
-}
-
-static int ibrowse_drv_control(ErlDrvData handle, unsigned int command,
-			       char *buf, int bufflen, char **rbuf, int rlen)
-{
-  State* state = (State *) handle;
-  unsigned int j = 0, i = 0;
-  unsigned int temp = 0, rlen_1 = 0;
-  char* replybuf;
-
-  fprintf(stderr, "alloc_ptr -> %d\n", state->alloc_ptr);
-/*   if(state->alloc_ptr != NULL) */
-/*     { */
-/*       driver_free(state->alloc_ptr); */
-/*     } */
-
-  /* Calculate encoded length. If same as bufflen, it means there is
-     no encoding to do. Do return an empty list */
-  rlen_1 = calc_encoded_length(buf, bufflen);
-  if(rlen_1 == bufflen)
-    {
-      *rbuf = NULL;
-      state->alloc_ptr = NULL;
-      return 0;
-    }
-  *rbuf = driver_alloc(rlen_1);
-  state->alloc_ptr = *rbuf;
-  fprintf(stderr, "*rbuf -> %d\n", *rbuf);
-  replybuf = *rbuf;
-
-  for(i=0;i<bufflen;i++)
-    {
-      temp = buf[i];
-      if( 'a' <= temp && temp <= 'z'
-	  || 'A' <= temp && temp <= 'Z'
-	  || '0' <= temp && temp <= '9'
-	  || temp == '-' || temp == '_' || temp == '.' )
-	{
-	  replybuf[j++] = temp;
-	  /*	  printf("j -> %d\n", j); */
-	}
-      else
-	{
-	  replybuf[j++] = 37;
-	  /* printf("temp -> %d\n", temp);
-	     printf("d2h(temp >> 4) -> %d\n", d2h(temp >> 4));
-	     printf("d2h(temp & 15) -> %d\n", d2h(temp & 15)); */
-	  replybuf[j++] = d2h(temp >> 4);
-	  replybuf[j++] = d2h(temp & 15);
-	  /*	  printf("j -> %d\n", j); */
-	}
-    }
-  return rlen_1;
-}
-
-/* Calculates the length of the resulting buffer if a string is URL encoded */
-int calc_encoded_length(char* buf, int bufflen)
-{
-  unsigned int count=0, i=0, temp=0;
-
-  for(i=0;i<bufflen;i++)
-    {
-      temp = buf[i];
-      if( 'a' <= temp && temp <= 'z'
-	  || 'A' <= temp && temp <= 'Z'
-	  || '0' <= temp && temp <= '9'
-	  || temp == '-' || temp == '_' || temp == '.' )
-	{
-	  count++;
-	}
-      else
-	{
-	  count = count+3;
-	}
-    }
-  return count;
-}
-
-/* Converts an integer in the range 1-15 into it's ascii value
-   1 -> 49 (ascii value of 1)
-   10 -> 97 (ascii value of a)
-*/
-int d2h(unsigned int i)
-{
-  if( i < 10 )
-    {
-      return i + 48;
-    }
-  else
-    {
-      return i + 97 - 10;
-    }
-}
diff --git a/include/ibrowse.hrl b/include/ibrowse.hrl
index ebf3bb3..18dde82 100644
--- a/include/ibrowse.hrl
+++ b/include/ibrowse.hrl
@@ -1,7 +1,16 @@
 -ifndef(IBROWSE_HRL).
 -define(IBROWSE_HRL, "ibrowse.hrl").
 
--record(url, {abspath, host, port, username, password, path, protocol}).
+-record(url, {
+          abspath,
+          host,
+          port,
+          username,
+          password,
+          path,
+          protocol,
+          host_type  % 'hostname', 'ipv4_address' or 'ipv6_address'
+}).
 
 -record(lb_pid, {host_port, pid}).
 
diff --git a/rebar.config b/rebar.config
new file mode 100644
index 0000000..a23b6e1
--- /dev/null
+++ b/rebar.config
@@ -0,0 +1,2 @@
+{erl_opts, [debug_info, warn_unused_vars, nowarn_shadow_vars, warn_unused_import]}.
+{eunit_opts, [verbose]}.
\ No newline at end of file
diff --git a/src/Makefile b/src/Makefile
deleted file mode 100644
index 145c0a2..0000000
--- a/src/Makefile
+++ /dev/null
@@ -1,32 +0,0 @@
-include ../vsn.mk
-
-ERL_FILES = ibrowse.erl \
-		ibrowse_http_client.erl \
-		ibrowse_app.erl \
-		ibrowse_sup.erl \
-		ibrowse_lib.erl \
-		ibrowse_lb.erl \
-		ibrowse_test.erl
-
-
-INCLUDE_DIRS = -I./ 
-
-ERLC ?= erlc
-ERLC_EMULATOR ?= erl -boot start_clean
-COMPILER_OPTIONS = -W +warn_unused_vars +nowarn_shadow_vars +warn_unused_import
-
-.SUFFIXES: .erl .beam $(SUFFIXES)
-EBIN = ../ebin
-
-all: $(ERL_FILES:%.erl=$(EBIN)/%.beam) ../ebin/ibrowse.app
-
-$(EBIN)/%.beam: %.erl
-	${ERLC} $(COMPILER_OPTIONS) $(INCLUDE_DIRS) -o ../ebin $<
-
-$(EBIN)/%.app:	%.app.src ../vsn.mk Makefile
-	sed -e s/%IBROWSE_VSN%/$(IBROWSE_VSN)/ \
-		 $< > $@
-
-clean:
-	rm -f $(EBIN)/*.beam $(EBIN)/*.app
-
diff --git a/src/ibrowse.erl b/src/ibrowse.erl
index e105150..d219212 100644
--- a/src/ibrowse.erl
+++ b/src/ibrowse.erl
@@ -6,8 +6,7 @@
 %%% Created : 11 Oct 2003 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
 %%%-------------------------------------------------------------------
 %% @author Chandrashekhar Mullaparthi <chandrashekhar dot mullaparthi at gmail dot com>
-%% @copyright 2005-2010 Chandrashekhar Mullaparthi
-%% @version 2.1.2
+%% @copyright 2005-2011 Chandrashekhar Mullaparthi
 %% @doc The ibrowse application implements an HTTP 1.1 client in erlang. This
 %% module implements the API of the HTTP client. There is one named
 %% process called 'ibrowse' which assists in load balancing and maintaining configuration. There is one load balancing process per unique webserver. There is
@@ -683,16 +682,16 @@
     State = #state{},
     put(my_trace_flag, State#state.trace),
     put(ibrowse_trace_token, "ibrowse"),
-    ets:new(ibrowse_lb, [named_table, public, {keypos, 2}]),
-    ets:new(ibrowse_conf, [named_table, protected, {keypos, 2}]),
-    ets:new(ibrowse_stream, [named_table, public]),
+    ibrowse_lb     = ets:new(ibrowse_lb, [named_table, public, {keypos, 2}]),
+    ibrowse_conf   = ets:new(ibrowse_conf, [named_table, protected, {keypos, 2}]),
+    ibrowse_stream = ets:new(ibrowse_stream, [named_table, public]),
     import_config(),
     {ok, #state{}}.
 
 import_config() ->
     case code:priv_dir(ibrowse) of
-        {error, _} = Err ->
-            Err;
+        {error, _} ->
+            ok;
         PrivDir ->
             Filename = filename:join(PrivDir, "ibrowse.conf"),
             import_config(Filename)
@@ -723,8 +722,8 @@
                           io:format("Skipping unrecognised term: ~p~n", [X])
                   end,
             lists:foreach(Fun, Terms);
-        Err ->
-            Err
+        _Err ->
+            ok
     end.
 
 %% @doc Internal export
diff --git a/src/ibrowse_http_client.erl b/src/ibrowse_http_client.erl
index 5dce321..eb2bf31 100644
--- a/src/ibrowse_http_client.erl
+++ b/src/ibrowse_http_client.erl
@@ -35,6 +35,7 @@
         ]).
 
 -include("ibrowse.hrl").
+-include_lib("kernel/include/inet.hrl").
 
 -record(state, {host, port, connect_timeout,
                 inactivity_timer_ref,
@@ -188,7 +189,7 @@
                                           cur_req = #request{req_id = Req_id}} = State) ->
     %% io:format("Client process set {active, once}~n", []),
     do_setopts(Socket, [{active, once}], State),
-    {noreply, State};
+    {noreply, set_inac_timer(State)};
 
 handle_info({stream_next, _Req_id}, State) ->
     _Cur_req_id = case State#state.cur_req of
@@ -216,12 +217,14 @@
     handle_sock_closed(State),
     {stop, normal, State};
 
-handle_info({tcp_error, _Sock}, State) ->
-    do_trace("Error on connection to ~1000.p:~1000.p~n", [State#state.host, State#state.port]),
+handle_info({tcp_error, _Sock, Reason}, State) ->
+    do_trace("Error on connection to ~1000.p:~1000.p -> ~1000.p~n",
+             [State#state.host, State#state.port, Reason]),
     handle_sock_closed(State),
     {stop, normal, State};
-handle_info({ssl_error, _Sock}, State) ->
-    do_trace("Error on SSL connection to ~1000.p:~1000.p~n", [State#state.host, State#state.port]),
+handle_info({ssl_error, _Sock, Reason}, State) ->
+    do_trace("Error on SSL connection to ~1000.p:~1000.p -> ~1000.p~n",
+             [State#state.host, State#state.port, Reason]),
     handle_sock_closed(State),
     {stop, normal, State};
 
@@ -334,8 +337,13 @@
                             active_once(State_1)
                     end,
                     State_2 = State_1#state{interim_reply_sent = false},
-                    State_3 = set_inac_timer(State_2),
-                    {noreply, State_3};
+                    case Ccs of
+                    true ->
+                        cancel_timer(State_2#state.inactivity_timer_ref, {eat_message, timeout}),
+                        {noreply, State_2#state{inactivity_timer_ref = undefined}};
+                    _ ->
+                        {noreply, set_inac_timer(State_2)}
+                    end;
                 State_1 ->
                     active_once(State_1),
                     State_2 = set_inac_timer(State_1),
@@ -461,7 +469,7 @@
                        undefined ->
                            Buf;
                        _ ->
-                           file:close(Fd),
+                           ok = file:close(Fd),
                            {file, TmpFilename}
                    end,
             Reply = case get_value(give_raw_headers, Options, false) of
@@ -470,11 +478,11 @@
                         false ->
                             {ok, SC, Headers, Buf}
                     end,
-            do_reply(State, From, StreamTo, ReqId, Resp_format, Reply),
-            do_error_reply(State#state{reqs = Reqs_1}, connection_closed),
-            State;
+            State_1 = do_reply(State, From, StreamTo, ReqId, Resp_format, Reply),
+            ok = do_error_reply(State_1#state{reqs = Reqs_1}, connection_closed),
+            State_1;
         _ ->
-            do_error_reply(State, connection_closed),
+            ok = do_error_reply(State, connection_closed),
             State
     end.
 
@@ -482,17 +490,40 @@
                                        use_proxy   = false,
                                        ssl_options = SSLOptions},
            Timeout) ->
-    Caller_socket_options = get_value(socket_options, Options, []),
-    Other_sock_options = filter_sock_options(SSLOptions ++ Caller_socket_options),
-    ssl:connect(Host, Port,
-                [binary, {nodelay, true}, {active, false} | Other_sock_options],
-                Timeout);
+    ssl:connect(Host, Port, get_sock_options(Host, Options, SSLOptions), Timeout);
 do_connect(Host, Port, Options, _State, Timeout) ->
+    gen_tcp:connect(Host, Port, get_sock_options(Host, Options, []), Timeout).
+
+get_sock_options(Host, Options, SSLOptions) ->
     Caller_socket_options = get_value(socket_options, Options, []),
-    Other_sock_options = filter_sock_options(Caller_socket_options),
-    gen_tcp:connect(Host, to_integer(Port),
-                    [binary, {nodelay, true}, {active, false} | Other_sock_options],
-                    Timeout).
+    Ipv6Options = case is_ipv6_host(Host) of
+        true ->
+            [inet6];
+        false ->
+            []
+    end,
+    Other_sock_options = filter_sock_options(SSLOptions ++ Caller_socket_options ++ Ipv6Options),
+    case lists:keysearch(nodelay, 1, Other_sock_options) of
+        false ->
+            [{nodelay, true}, binary, {active, false} | Other_sock_options];
+        {value, _} ->
+            [binary, {active, false} | Other_sock_options]
+    end.
+
+is_ipv6_host(Host) ->
+    case inet_parse:address(Host) of
+        {ok, {_, _, _, _, _, _, _, _}} ->
+            true;
+        {ok, {_, _, _, _}} ->
+            false;
+        _  ->
+            case inet:gethostbyname(Host) of
+                {ok, #hostent{h_addrtype = inet6}} ->
+                    true;
+                _ ->
+                    false
+            end
+    end.
 
 %% We don't want the caller to specify certain options
 filter_sock_options(Opts) ->
@@ -547,7 +578,7 @@
 maybe_chunked_encode(Data, false) ->
     Data;
 maybe_chunked_encode(Data, true) ->
-    [?dec2hex(size(to_binary(Data))), "\r\n", Data, "\r\n"].
+    [?dec2hex(iolist_size(Data)), "\r\n", Data, "\r\n"].
 
 do_close(#state{socket = undefined})            ->  ok;
 do_close(#state{socket = Sock,
@@ -1269,7 +1300,12 @@
                        reply_buffer  = RepBuf,
                        recvd_headers = RespHeaders}=State) when SaveResponseToFile /= false ->
     Body = RepBuf,
-    file:close(Fd),
+    case Fd of
+        undefined -> 
+            ok;
+        _ -> 
+            ok = file:close(Fd)
+    end,
     ResponseBody = case TmpFilename of
                        undefined ->
                            Body;
@@ -1656,8 +1692,8 @@
     {_, Reqs_1} = queue:out(Reqs),
     #request{from=From, stream_to=StreamTo, req_id=ReqId,
              response_format = Resp_format} = CurReq,
-    do_reply(State, From, StreamTo, ReqId, Resp_format, Reply),
-    do_error_reply(State#state{reqs = Reqs_1}, previous_request_failed).
+    State_1 = do_reply(State, From, StreamTo, ReqId, Resp_format, Reply),
+    do_error_reply(State_1#state{reqs = Reqs_1}, previous_request_failed).
 
 split_list_at(List, N) ->
     split_list_at(List, N, []).
@@ -1701,7 +1737,8 @@
 to_ascii($0) -> 0.
 
 cancel_timer(undefined) -> ok;
-cancel_timer(Ref)       -> erlang:cancel_timer(Ref).
+cancel_timer(Ref)       -> _ = erlang:cancel_timer(Ref),
+                           ok.
 
 cancel_timer(Ref, {eat_message, Msg}) ->
     cancel_timer(Ref),
@@ -1814,8 +1851,5 @@
             ok
     end.
 
-to_integer(X) when is_list(X)    -> list_to_integer(X); 
-to_integer(X) when is_integer(X) -> X.
-
 to_binary(X) when is_list(X)   -> list_to_binary(X); 
 to_binary(X) when is_binary(X) -> X.
diff --git a/src/ibrowse_lib.erl b/src/ibrowse_lib.erl
index 696d0f6..3cbe3ac 100644
--- a/src/ibrowse_lib.erl
+++ b/src/ibrowse_lib.erl
@@ -180,7 +180,19 @@
     V.
 
 parse_url(Url) ->
-    parse_url(Url, get_protocol, #url{abspath=Url}, []).
+    case parse_url(Url, get_protocol, #url{abspath=Url}, []) of
+        #url{host_type = undefined, host = Host} = UrlRec ->
+            case inet_parse:address(Host) of
+                {ok, {_, _, _, _, _, _, _, _}} ->
+                    UrlRec#url{host_type = ipv6_address};
+                {ok, {_, _, _, _}} ->
+                    UrlRec#url{host_type = ipv4_address};
+                _ ->
+                    UrlRec#url{host_type = hostname}
+            end;
+        Else ->
+            Else
+    end.
 
 parse_url([$:, $/, $/ | _], get_protocol, Url, []) ->
     {invalid_uri_1, Url};
@@ -215,6 +227,21 @@
               Url#url{username = lists:reverse(TmpAcc),
                       password = ""},
               []);
+parse_url([$[ | T], get_username, Url, []) ->
+    % IPv6 address literals are enclosed by square brackets:
+    %     http://www.ietf.org/rfc/rfc2732.txt
+    parse_url(T, get_ipv6_address, Url#url{host_type = ipv6_address}, []);
+parse_url([$[ | T], get_username, _Url, TmpAcc) ->
+    {error, {invalid_username_or_host, lists:reverse(TmpAcc) ++ "[" ++ T}};
+parse_url([$[ | _], get_password, _Url, []) ->
+    {error, missing_password};
+parse_url([$[ | T], get_password, Url, TmpAcc) ->
+    % IPv6 address literals are enclosed by square brackets:
+    %     http://www.ietf.org/rfc/rfc2732.txt
+    parse_url(T, get_ipv6_address,
+              Url#url{host_type = ipv6_address,
+                      password = lists:reverse(TmpAcc)},
+              []);
 parse_url([$@ | T], get_password, Url, TmpAcc) ->
     parse_url(T, get_host, 
               Url#url{password = lists:reverse(TmpAcc)},
@@ -236,6 +263,28 @@
             username = undefined,
             password = undefined,
            path = Path};
+parse_url([$] | T], get_ipv6_address, #url{protocol = Prot} = Url, TmpAcc) ->
+    Addr = lists:reverse(TmpAcc),
+    case inet_parse:address(Addr) of
+        {ok, {_, _, _, _, _, _, _, _}} ->
+            Url2 = Url#url{host = Addr, port = default_port(Prot)},
+            case T of
+                [$: | T2] ->
+                    parse_url(T2, get_port, Url2, []);
+                [$/ | T2] ->
+                    Url2#url{path = [$/ | T2]};
+                [$? | T2] ->
+                    Url2#url{path = [$/, $? | T2]};
+                [] ->
+                    Url2#url{path = "/"};
+                _ ->
+                    {error, {invalid_host, "[" ++ Addr ++ "]" ++ T}}
+            end;
+        _ ->
+            {error, {invalid_ipv6_address, Addr}}
+    end;
+parse_url([$[ | T], get_host, #url{} = Url, []) ->
+    parse_url(T, get_ipv6_address, Url#url{host_type = ipv6_address}, []);
 parse_url([$: | T], get_host, #url{} = Url, TmpAcc) ->
     parse_url(T, get_port, 
               Url#url{host = lists:reverse(TmpAcc)},
diff --git a/src/ibrowse_test.erl b/src/ibrowse_test.erl
index 757932f..502806b 100644
--- a/src/ibrowse_test.erl
+++ b/src/ibrowse_test.erl
@@ -11,17 +11,16 @@
 	 unit_tests/0,
 	 unit_tests/1,
 	 unit_tests_1/2,
-	 test_drv_ue/0,
-	 test_drv_ue/1,
-	 test_ue/0,
-	 test_ue/1,
+	 ue_test/0,
+	 ue_test/1,
 	 verify_chunked_streaming/0,
 	 verify_chunked_streaming/1,
          test_chunked_streaming_once/0,
 	 i_do_async_req_list/4,
 	 test_stream_once/3,
 	 test_stream_once/4,
-         test_20122010/0
+         test_20122010/0,
+         test_20122010/1
 	]).
 
 test_stream_once(Url, Method, Options) ->
@@ -413,23 +412,9 @@
 	    io:format("~p~n", [Err])
     end.
 
-test_drv_ue() ->
-    test_drv_ue(lists:duplicate(1024, 127)).
-test_drv_ue(Data) ->
-    [{port, Port}| _] = ets:lookup(ibrowse_table, port),
-%     erl_ddll:unload_driver("ibrowse_drv"),
-%     timer:sleep(1000),
-%     erl_ddll:load_driver("../priv", "ibrowse_drv"),
-%     Port = open_port({spawn, "ibrowse_drv"}, []),
-    {Time, Res} = timer:tc(ibrowse_lib, drv_ue, [Data, Port]),
-    io:format("Time -> ~p~n", [Time]),
-    io:format("Data Length -> ~p~n", [length(Data)]),
-    io:format("Res Length -> ~p~n", [length(Res)]).
-%    io:format("Result -> ~s~n", [Res]).
-
-test_ue() ->
-    test_ue(lists:duplicate(1024, $?)).
-test_ue(Data) ->
+ue_test() ->
+    ue_test(lists:duplicate(1024, $?)).
+ue_test(Data) ->
     {Time, Res} = timer:tc(ibrowse_lib, url_encode, [Data]),
     io:format("Time -> ~p~n", [Time]),
     io:format("Data Length -> ~p~n", [length(Data)]),
@@ -445,11 +430,14 @@
 %%------------------------------------------------------------------------------
 
 test_20122010() ->
-    {ok, Pid} = ibrowse:spawn_worker_process("http://localhost:8181"),
+    test_20122010("http://localhost:8181").
+
+test_20122010(Url) ->
+    {ok, Pid} = ibrowse:spawn_worker_process(Url),
     Expected_resp = <<"1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24-25-26-27-28-29-30-31-32-33-34-35-36-37-38-39-40-41-42-43-44-45-46-47-48-49-50-51-52-53-54-55-56-57-58-59-60-61-62-63-64-65-66-67-68-69-70-71-72-73-74-75-76-77-78-79-80-81-82-83-84-85-86-87-88-89-90-91-92-93-94-95-96-97-98-99-100">>,
     Test_parent = self(),
     Fun = fun() ->
-                  do_test_20122010(Pid, Expected_resp, Test_parent)
+                  do_test_20122010(Url, Pid, Expected_resp, Test_parent)
           end,
     Pids = [erlang:spawn_monitor(Fun) || _ <- lists:seq(1,10)],
     wait_for_workers(Pids).
@@ -458,18 +446,24 @@
     receive
         {Pid, success} ->
             wait_for_workers(Pids)
-    after 5000 ->
+    after 60000 ->
             test_failed
     end;
 wait_for_workers([]) ->
     success.
 
-do_test_20122010(Pid, Expected_resp, Test_parent) ->
+do_test_20122010(Url, Pid, Expected_resp, Test_parent) ->
+    do_test_20122010(10, Url, Pid, Expected_resp, Test_parent).
+
+do_test_20122010(0, _Url, _Pid, _Expected_resp, Test_parent) ->
+    Test_parent ! {self(), success};
+do_test_20122010(Rem_count, Url, Pid, Expected_resp, Test_parent) ->
     {ibrowse_req_id, Req_id} = ibrowse:send_req_direct(
                                  Pid,
-                                 "http://localhost:8181/ibrowse_stream_once_chunk_pipeline_test",
+                                 Url ++ "/ibrowse_stream_once_chunk_pipeline_test",
                                  [], get, [],
                                  [{stream_to, {self(), once}},
+                                  {inactivity_timeout, 10000},
                                   {include_ibrowse_req_id, true}]),
     do_trace("~p -- sent request ~1000.p~n", [self(), Req_id]),
     Req_id_str = lists:flatten(io_lib:format("~1000.p",[Req_id])),
@@ -491,7 +485,7 @@
     ok = ibrowse:stream_next(Req_id),
     case do_test_20122010_1(Expected_resp, Req_id, []) of
         true ->
-            Test_parent ! {self(), success};
+            do_test_20122010(Rem_count - 1, Url, Pid, Expected_resp, Test_parent);
         false ->
             Test_parent ! {self(), failed}
     end.
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 0000000..2851ed2
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,19 @@
+ERL_FILES = ibrowse_test_server.erl
+
+
+INCLUDE_DIRS = -I./ 
+
+ERLC ?= erlc
+ERLC_EMULATOR ?= erl -boot start_clean
+COMPILER_OPTIONS = -W +warn_unused_vars +nowarn_shadow_vars +warn_unused_import
+
+.SUFFIXES: .erl .beam $(SUFFIXES)
+
+all: $(ERL_FILES:%.erl=%.beam)
+
+%.beam: %.erl
+	${ERLC} $(COMPILER_OPTIONS) $(INCLUDE_DIRS) -o ./ $<
+
+clean:
+	rm -f *.beam 
+
diff --git a/test/ibrowse_lib_tests.erl b/test/ibrowse_lib_tests.erl
new file mode 100644
index 0000000..6f613e9
--- /dev/null
+++ b/test/ibrowse_lib_tests.erl
@@ -0,0 +1,135 @@
+%%% File    : ibrowse_lib.erl
+%%% Authors : Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>,
+%%%           Filipe David Manana <fdmanana@apache.org>
+%%% Description : Tests for the module ibrowse_lib.erl
+%%% Created : 12 April 2011 by Filipe David Manana <fdmanana@apache.org>
+
+-module(ibrowse_lib_tests).
+-include_lib("eunit/include/eunit.hrl").
+-include_lib("ibrowse/include/ibrowse.hrl").
+
+
+parse_urls_test_() ->
+    {timeout, 60, [fun parse_urls/0]}.
+
+
+parse_urls() ->
+    ?assertMatch(#url{
+                    abspath = "http://localhost",
+                    host = "localhost",
+                    host_type = hostname,
+                    port = 80,
+                    path = "/",
+                    username = undefined,
+                    password = undefined,
+                    protocol = http
+                   },
+                 ibrowse_lib:parse_url("http://localhost")),
+    ?assertMatch(#url{
+                    abspath = "http://localhost:80/",
+                    host = "localhost",
+                    host_type = hostname,
+                    port = 80,
+                    path = "/",
+                    username = undefined,
+                    password = undefined,
+                    protocol = http
+                   },
+                 ibrowse_lib:parse_url("http://localhost:80/")),
+    ?assertMatch(#url{
+                    abspath = "http://127.0.0.1:8000/",
+                    host = "127.0.0.1",
+                    host_type = ipv4_address,
+                    port = 8000,
+                    path = "/",
+                    username = undefined,
+                    password = undefined,
+                    protocol = http
+                   },
+                 ibrowse_lib:parse_url("http://127.0.0.1:8000/")),
+    ?assertMatch(#url{
+                    abspath = "https://foo:bar@127.0.0.1:8000/test",
+                    host = "127.0.0.1",
+                    host_type = ipv4_address,
+                    port = 8000,
+                    path = "/test",
+                    username = "foo",
+                    password = "bar",
+                    protocol = https
+                   },
+                 ibrowse_lib:parse_url("https://foo:bar@127.0.0.1:8000/test")),
+    ?assertMatch(#url{
+                    abspath = "https://[::1]",
+                    host = "::1",
+                    host_type = ipv6_address,
+                    port = 443,
+                    path = "/",
+                    username = undefined,
+                    password = undefined,
+                    protocol = https
+                   },
+                 ibrowse_lib:parse_url("https://[::1]")),
+    ?assertMatch(#url{
+                    abspath = "http://[::1]:8080",
+                    host = "::1",
+                    host_type = ipv6_address,
+                    port = 8080,
+                    path = "/",
+                    username = undefined,
+                    password = undefined,
+                    protocol = http
+                   },
+                 ibrowse_lib:parse_url("http://[::1]:8080")),
+    ?assertMatch(#url{
+                    abspath = "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:8081/index.html",
+                    host = "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210",
+                    host_type = ipv6_address,
+                    port = 8081,
+                    path = "/index.html",
+                    username = undefined,
+                    password = undefined,
+                    protocol = http
+                   },
+                 ibrowse_lib:parse_url("http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:8081/index.html")),
+    ?assertMatch(#url{
+                    abspath = "http://[1080:0:0:0:8:800:200C:417A]/foo/bar",
+                    host = "1080:0:0:0:8:800:200C:417A",
+                    host_type = ipv6_address,
+                    port = 80,
+                    path = "/foo/bar",
+                    username = undefined,
+                    password = undefined,
+                    protocol = http
+                   },
+                 ibrowse_lib:parse_url("http://[1080:0:0:0:8:800:200C:417A]/foo/bar")),
+    ?assertMatch(#url{
+                    abspath = "http://[1080:0:0:0:8:800:200C:417A]:8080/foo/bar",
+                    host = "1080:0:0:0:8:800:200C:417A",
+                    host_type = ipv6_address,
+                    port = 8080,
+                    path = "/foo/bar",
+                    username = undefined,
+                    password = undefined,
+                    protocol = http
+                   },
+                 ibrowse_lib:parse_url("http://[1080:0:0:0:8:800:200C:417A]:8080/foo/bar")),
+    ?assertMatch(#url{
+                    abspath = "http://[::192.9.5.5]:6000/foo?q=bar",
+                    host = "::192.9.5.5",
+                    host_type = ipv6_address,
+                    port = 6000,
+                    path = "/foo?q=bar",
+                    username = undefined,
+                    password = undefined,
+                    protocol = http
+                   },
+                 ibrowse_lib:parse_url("http://[::192.9.5.5]:6000/foo?q=bar")),
+    ?assertMatch({error, {invalid_ipv6_address, ":1080:0:0:0:8:800:200C:417A:"}},
+                 ibrowse_lib:parse_url("http://[:1080:0:0:0:8:800:200C:417A:]:6000/foo?q=bar")),
+    ?assertMatch({error, {invalid_ipv6_address, "12::z"}},
+                 ibrowse_lib:parse_url("http://[12::z]")),
+    ?assertMatch({error, {invalid_username_or_host, _}},
+                 ibrowse_lib:parse_url("http://foo[1080:0:0:0:8:800:200C:417A]:6000")),
+    ?assertMatch({error, missing_password},
+                 ibrowse_lib:parse_url("http://foo:[1080:0:0:0:8:800:200C:417A]:6000")),
+    ok.
diff --git a/test/ibrowse_test_server.erl b/test/ibrowse_test_server.erl
index d3b66bc..45c6958 100644
--- a/test/ibrowse_test_server.erl
+++ b/test/ibrowse_test_server.erl
@@ -17,18 +17,22 @@
     Fun = fun() ->
                   register(server_proc_name(Port), self()),
                   case do_listen(Sock_type, Port, [{active, false},
+                                                   {reuseaddr, true},
                                                    {nodelay, true},
                                                    {packet, http}]) of
                       {ok, Sock} ->
                           do_trace("Server listening on port: ~p~n", [Port]),
                           accept_loop(Sock, Sock_type);
                       Err ->
-                          do_trace("Failed to start server on port ~p. ~p~n",
-                                   [Port, Err]),
-                          Err
+                          erlang:error(
+                            lists:flatten(
+                              io_lib:format(
+                                "Failed to start server on port ~p. ~p~n",
+                                [Port, Err]))),
+                          exit({listen_error, Err})
                   end
           end,
-    spawn(Fun).
+    spawn_link(Fun).
 
 stop_server(Port) ->
     exit(whereis(server_proc_name(Port)), kill).
diff --git a/vsn.mk b/vsn.mk
deleted file mode 100644
index ac4bf7a..0000000
--- a/vsn.mk
+++ /dev/null
@@ -1,2 +0,0 @@
-IBROWSE_VSN = 2.1.2
-