Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add support for replication over IPv6 (part 1)

This change upgrades ibrowse to version 2.2.0. This version adds support
for IPv6 (cmullaparthi/ibrowse#34).
This is part of COUCHDB-665.



git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@1091709 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information...
commit 1ca42d229fe5c478311c348ee115cd31b46a2841 1 parent baa0e30
@fdmanana fdmanana authored
View
2  src/ibrowse/Makefile.am
@@ -10,7 +10,7 @@
## License for the specific language governing permissions and limitations under
## the License.
-ibrowseebindir = $(localerlanglibdir)/ibrowse-2.1.3/ebin
+ibrowseebindir = $(localerlanglibdir)/ibrowse-2.2.0/ebin
ibrowse_file_collection = \
ibrowse.app.in \
View
2  src/ibrowse/ibrowse.app.in
@@ -1,6 +1,6 @@
{application, ibrowse,
[{description, "HTTP client application"},
- {vsn, "2.1.3"},
+ {vsn, "2.2.0"},
{modules, [ ibrowse,
ibrowse_http_client,
ibrowse_app,
View
11 src/ibrowse/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}).
View
37 src/ibrowse/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,
@@ -489,13 +490,19 @@ do_connect(Host, Port, Options, #state{is_ssl = true,
use_proxy = false,
ssl_options = SSLOptions},
Timeout) ->
- ssl:connect(Host, Port, get_sock_options(Options, SSLOptions), 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(Options, []), Timeout).
+ gen_tcp:connect(Host, Port, get_sock_options(Host, Options, []), Timeout).
-get_sock_options(Options, SSLOptions) ->
+get_sock_options(Host, Options, SSLOptions) ->
Caller_socket_options = get_value(socket_options, Options, []),
- Other_sock_options = filter_sock_options(SSLOptions ++ Caller_socket_options),
+ 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];
@@ -503,6 +510,21 @@ get_sock_options(Options, SSLOptions) ->
[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) ->
lists:filter(fun({active, _}) ->
@@ -1278,7 +1300,12 @@ handle_response(#request{from=From, stream_to=StreamTo, req_id=ReqId,
reply_buffer = RepBuf,
recvd_headers = RespHeaders}=State) when SaveResponseToFile /= false ->
Body = RepBuf,
- ok = file:close(Fd),
+ case Fd of
+ undefined ->
+ ok;
+ _ ->
+ ok = file:close(Fd)
+ end,
ResponseBody = case TmpFilename of
undefined ->
Body;
View
51 src/ibrowse/ibrowse_lib.erl
@@ -180,7 +180,19 @@ get_value(Tag, TVL) ->
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 @@ parse_url([$@ | T], get_username, Url, TmpAcc) ->
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 @@ parse_url([H | T], get_password, Url, TmpAcc) when H == $/;
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)},
Please sign in to comment.
Something went wrong with that request. Please try again.