diff options
Diffstat (limited to 'src/couch_inets/http_transport.erl')
-rw-r--r-- | src/couch_inets/http_transport.erl | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/src/couch_inets/http_transport.erl b/src/couch_inets/http_transport.erl new file mode 100644 index 00000000..57787ef3 --- /dev/null +++ b/src/couch_inets/http_transport.erl @@ -0,0 +1,291 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +% +-module(http_transport). + +% Internal application API +-export([start/1, connect/3, connect/4, listen/2, listen/3, + accept/2, accept/3, close/2, + send/3, controlling_process/3, setopts/3, + peername/2, resolve/0]). + +%%%========================================================================= +%%% Internal application API +%%%========================================================================= + +%%------------------------------------------------------------------------- +%% start(SocketType) -> ok | {error, Reason} +%% SocketType = ip_comm | {ssl, _} +%% +%% Description: Makes sure inet_db or ssl is started. +%%------------------------------------------------------------------------- +start(ip_comm) -> + case inet_db:start() of + {ok, _} -> + ok; + {error, {already_started, _}} -> + ok; + Error -> + Error + end; +start({ssl, _}) -> + case ssl:start() of + ok -> + ok; + {ok, _} -> + ok; + {error, {already_started,_}} -> + ok; + Error -> + Error + end. + +%%------------------------------------------------------------------------- +%% connect(SocketType, Address, IPV6, Timeout) -> +%% {ok, Socket} | {error, Reason} +%% SocketType = ip_comm | {ssl, SslConfig} +%% Address = {Host, Port} +%% IPV6 = disabled | enabled +%% Socket = socket() +%% +%% Description: Connects to the Host and Port specified in HTTPRequest. +%% uses ipv6 if possible. +%%------------------------------------------------------------------------- +connect(SocketType, Address, IPV6) -> + connect(SocketType, Address, IPV6, infinity). + +connect(ip_comm, {Host, Port}, enabled, Timeout) -> + {Opts, NewHost} = + case inet:getaddr(Host, inet6) of + {ok, IPAddr = {0, 0, 0, 0, 0, 16#ffff, _, _}} -> + case inet:getaddr(Host, inet) of + {ok,NewIP} -> + {[binary, {packet, 0}, {active, false}, + {reuseaddr,true}], NewIP}; + _Error -> + {[binary, {packet, 0}, {active, false}, + {reuseaddr,true}, inet6], IPAddr} + end; + {ok, IPAddr} -> + {[binary, {packet, 0}, {active, false}, + {reuseaddr,true}, inet6], IPAddr}; + _ -> + {[binary, {packet, 0}, {active, false}, + {reuseaddr,true}], Host} + end, + gen_tcp:connect(NewHost, Port, Opts, Timeout); + +connect(ip_comm, {Host, Port}, disabled, Timeout) -> + Opts = [binary, {packet, 0}, {active, false}, {reuseaddr,true}], + gen_tcp:connect(Host, Port, Opts, Timeout); + +connect({ssl, SslConfig}, {Host, Port}, _, Timeout) -> + Opts = [binary, {active, false}] ++ SslConfig, + ssl:connect(Host, Port, Opts, Timeout). + +%%------------------------------------------------------------------------- +%% listen(SocketType, Port) -> {ok, Socket} | {error, Reason} +%% SocketType = ip_comm | {ssl, SSLConfig} +%% Port = integer() +%% Socket = socket() +%% +%% Description: Sets up socket to listen on the port Port on the local +%% host using either gen_tcp or ssl. In the gen_tcp case the port +%% might allready have been initiated by a wrapper-program and is +%% given as an Fd that can be retrieved by init:get_argument. The +%% reason for this to enable a HTTP-server not runnig as root to use +%% port 80. +%%------------------------------------------------------------------------- +listen(SocketType, Port) -> + listen(SocketType, undefined, Port). + +listen(ip_comm, Addr, Port) -> + FdName = list_to_atom("httpd_" ++ integer_to_list(Port)), + {NewPort, Opt} = + case init:get_argument(FdName) of + {ok, [[FdStr]]} -> + Fd = list_to_integer(FdStr), + {0, + sock_opt(ip_comm, Addr, [{backlog, 128}, + {reuseaddr,true}, {fd,Fd}, {nodelay, true}])}; + error -> + {Port, + sock_opt(ip_comm, Addr, + [{backlog, 128}, {reuseaddr, true}, {nodelay, true}])} + end, + gen_tcp:listen(NewPort, Opt); + +listen({ssl, SSLConfig} = Ssl, Addr, Port) -> + Opt = sock_opt(Ssl, Addr, SSLConfig), + ssl:listen(Port, Opt). + +%%------------------------------------------------------------------------- +%% accept(SocketType, ListenSocket) -> {ok, Socket} | {error, Reason} +%% accept(SocketType, ListenSocket, Timeout) -> ok | {error, Reason} +%% SocketType = ip_comm | {ssl, SSLConfig} +%% ListenSocket = socket() +%% Timeout = infinity | integer() >= 0 +%% Socket = socket() +%% +%% Description: Accepts an incoming connection request on a listen socket, +%% using either gen_tcp or ssl. +%%------------------------------------------------------------------------- +accept(SocketType, ListenSocket) -> + accept(SocketType, ListenSocket, infinity). +accept(ip_comm, ListenSocket, Timeout) -> + gen_tcp:accept(ListenSocket, Timeout); +accept({ssl,_SSLConfig}, ListenSocket, Timeout) -> + ssl:accept(ListenSocket, Timeout). + +%%------------------------------------------------------------------------- +%% controlling_process(SocketType, Socket, NewOwner) -> ok | {error, Reason} +%% SocketType = ip_comm | {ssl, _} +%% Socket = socket() +%% NewOwner = pid() +%% +%% Description: Assigns a new controlling process to Socket. +%%------------------------------------------------------------------------- +controlling_process(ip_comm, Socket, NewOwner) -> + gen_tcp:controlling_process(Socket, NewOwner); +controlling_process({ssl, _}, Socket, NewOwner) -> + ssl:controlling_process(Socket, NewOwner). + +%%------------------------------------------------------------------------- +%% setopts(SocketType, Socket, Options) -> ok | {error, Reason} +%% SocketType = ip_comm | {ssl, _} +%% Socket = socket() +%% Options = list() +%% Description: Sets one or more options for a socket, using either +%% gen_tcp or ssl. +%%------------------------------------------------------------------------- +setopts(ip_comm, Socket, Options) -> + inet:setopts(Socket,Options); +setopts({ssl, _}, Socket, Options) -> + ssl:setopts(Socket, Options). + +%%------------------------------------------------------------------------- +%% send(RequestOrSocketType, Socket, Message) -> ok | {error, Reason} +%% SocketType = ip_comm | {ssl, _} +%% Socket = socket() +%% Message = list() | binary() +%% Description: Sends a packet on a socket, using either gen_tcp or ssl. +%%------------------------------------------------------------------------- +send(ip_comm, Socket, Message) -> + gen_tcp:send(Socket, Message); +send({ssl, _}, Socket, Message) -> + ssl:send(Socket, Message). + +%%------------------------------------------------------------------------- +%% close(SocketType, Socket) -> ok | {error, Reason} +%% SocketType = ip_comm | {ssl, _} +%% Socket = socket() +%% +%% Description: Closes a socket, using either gen_tcp or ssl. +%%------------------------------------------------------------------------- +close(ip_comm, Socket) -> + gen_tcp:close(Socket); +close({ssl, _}, Socket) -> + ssl:close(Socket). + +%%------------------------------------------------------------------------- +%% peername(SocketType, Socket) -> ok | {error, Reason} +%% SocketType = ip_comm | {ssl, _} +%% Socket = socket() +%% +%% Description: Returns the address and port for the other end of a +%% connection, usning either gen_tcp or ssl. +%%------------------------------------------------------------------------- +peername(ip_comm, Socket) -> + case inet:peername(Socket) of + {ok,{{A, B, C, D}, Port}} -> + PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++ + integer_to_list(C)++"."++integer_to_list(D), + {Port, PeerName}; + {ok,{{A, B, C, D, E, F, G, H}, Port}} -> + PeerName = http_util:integer_to_hexlist(A) ++ ":"++ + http_util:integer_to_hexlist(B) ++ ":" ++ + http_util:integer_to_hexlist(C) ++ ":" ++ + http_util:integer_to_hexlist(D) ++ ":" ++ + http_util:integer_to_hexlist(E) ++ ":" ++ + http_util:integer_to_hexlist(F) ++ ":" ++ + http_util:integer_to_hexlist(G) ++":"++ + http_util:integer_to_hexlist(H), + {Port, PeerName}; + {error, _} -> + {-1, "unknown"} + end; + +peername({ssl, _}, Socket) -> + case ssl:peername(Socket) of + {ok,{{A, B, C, D}, Port}} -> + PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++ + integer_to_list(C)++"."++integer_to_list(D), + {Port, PeerName}; + {error, _} -> + {-1, "unknown"} + end. + +%%------------------------------------------------------------------------- +%% resolve() -> HostName +%% HostName = string() +%% +%% Description: Returns the local hostname. +%%------------------------------------------------------------------------- +resolve() -> + {ok, Name} = inet:gethostname(), + Name. + + +%%%======================================================================== +%%% Internal functions +%%%======================================================================== + +%% Address any comes from directive: BindAddress "*" +sock_opt(ip_comm, any = Addr, Opt) -> + sock_opt1([{ip, Addr} | Opt]); +sock_opt(ip_comm, undefined, Opt) -> + sock_opt1(Opt); +sock_opt(_, any = Addr, Opt) -> + sock_opt2([{ip, Addr} | Opt]); +sock_opt(_, undefined, Opt) -> + sock_opt2(Opt); +sock_opt(_, Addr, Opt) when size(Addr) == 4 -> + sock_opt2([{ip, Addr} | Opt]); +sock_opt(ip_comm, Addr, Opt) -> + sock_opt2([inet6, {ip, Addr} | Opt]); +sock_opt(_, Addr, Opt) -> + sock_opt2([{ip, Addr} | Opt]). + +sock_opt1(Opt) -> + case has_inet6_supported() of + yes -> + sock_opt2([inet6 | Opt]); + no -> + sock_opt2(Opt) + end. + +sock_opt2(Opt) -> + [{packet, 0}, {active, false} | Opt]. + +has_inet6_supported() -> + case (catch inet:getaddr("localhost", inet6)) of + {ok, {0, 0, 0, 0, 0, 16#ffff, _, _}} -> + no; + {ok,_} -> yes; + _ -> + no + end. |