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 - Youns Hafri reported a bug where ibrowse was not returning the
+13-11-2006 - Youns 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 - Youns Hafri made a CRUX LINUX port of ibrowse.
+08-May-2005 - Youns 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
-