aboutsummaryrefslogtreecommitdiffstats
path: root/lib/inets/src/http_lib/http_transport.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/inets/src/http_lib/http_transport.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/inets/src/http_lib/http_transport.erl')
-rw-r--r--lib/inets/src/http_lib/http_transport.erl353
1 files changed, 353 insertions, 0 deletions
diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl
new file mode 100644
index 0000000000..8100d7183a
--- /dev/null
+++ b/lib/inets/src/http_lib/http_transport.erl
@@ -0,0 +1,353 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+%
+-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]).
+
+-export([negotiate/3]).
+
+
+%%%=========================================================================
+%%% 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;
+ {error, {already_started,_}} ->
+ ok;
+ Error ->
+ Error
+ end.
+
+
+%%-------------------------------------------------------------------------
+%% connect(SocketType, Address, Options, Timeout) ->
+%% {ok, Socket} | {error, Reason}
+%% SocketType = ip_comm | {ssl, SslConfig}
+%% Address = {Host, Port}
+%% Options = [option()]
+%% Socket = socket()
+%% option() = ipfamily() | {ip, ip_address()} | {port, integer()}
+%% ipfamily() = inet | inet6
+%%
+%% Description: Connects to the Host and Port specified in HTTPRequest.
+%%-------------------------------------------------------------------------
+
+connect(SocketType, Address, Opts) ->
+ connect(SocketType, Address, Opts, infinity).
+
+connect(ip_comm = _SocketType, {Host, Port}, Opts0, Timeout)
+ when is_list(Opts0) ->
+ Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0],
+ gen_tcp:connect(Host, Port, Opts, Timeout);
+
+connect({ssl, SslConfig}, {Host, Port}, _, Timeout) ->
+ Opts = [binary, {active, false}] ++ SslConfig,
+ ssl:connect(Host, Port, Opts, Timeout);
+
+connect({erl_ssl, SslConfig}, {Host, Port}, _, Timeout) ->
+ Opts = [binary, {active, false}, {ssl_imp, new}] ++ 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 running as root to use
+%% port 80.
+%%-------------------------------------------------------------------------
+listen(SocketType, Port) ->
+ listen(SocketType, undefined, Port).
+
+listen(ip_comm, Addr, Port) ->
+ case (catch listen_ip_comm(Addr, Port)) of
+ {'EXIT', Reason} ->
+ {error, {exit, Reason}};
+ Else ->
+ Else
+ end;
+
+listen({ssl, SSLConfig} = Ssl, Addr, Port) ->
+ Opt = sock_opt(Ssl, Addr, SSLConfig),
+ ssl:listen(Port, Opt);
+
+listen({erl_ssl, SSLConfig} = Ssl, Addr, Port) ->
+ Opt = sock_opt(Ssl, Addr, SSLConfig),
+ ssl:listen(Port, [{ssl_imp, new} | Opt]).
+
+
+listen_ip_comm(Addr, Port) ->
+ {NewPort, Opts, IpFamily} = get_socket_info(Addr, Port),
+ case IpFamily of
+ inet6fb4 ->
+ Opts2 = [inet6 | Opts],
+ case (catch gen_tcp:listen(NewPort, Opts2)) of
+ {error, Reason} when ((Reason =:= nxdomain) orelse
+ (Reason =:= eafnosupport)) ->
+ Opts3 = [inet | Opts],
+ gen_tcp:listen(NewPort, Opts3);
+
+ %% This is when a given hostname has resolved to a
+ %% IPv4-address. The inet6-option together with a
+ %% {ip, IPv4} option results in badarg
+ {'EXIT', _} ->
+ Opts3 = [inet | Opts],
+ gen_tcp:listen(NewPort, Opts3);
+
+ Other ->
+ Other
+ end;
+ _ ->
+ Opts2 = [IpFamily | Opts],
+ gen_tcp:listen(NewPort, Opts2)
+ end.
+
+ipfamily_default(Addr, Port) ->
+ httpd_conf:lookup(Addr, Port, ipfamily, inet6fb4).
+
+get_socket_info(Addr, Port) ->
+ Key = list_to_atom("httpd_" ++ integer_to_list(Port)),
+ BaseOpts = [{backlog, 128}, {reuseaddr, true}],
+ IpFamilyDefault = ipfamily_default(Addr, Port),
+ case init:get_argument(Key) of
+ {ok, [[Value]]} ->
+ {Fd, IpFamily} =
+ case string:tokens(Value, [$|]) of
+ [FdStr, IpFamilyStr] ->
+ Fd0 = fd_of(FdStr),
+ IpFamily0 = ip_family_of(IpFamilyStr),
+ {Fd0, IpFamily0};
+ [FdStr] ->
+ {fd_of(FdStr), IpFamilyDefault};
+ _ ->
+ throw({error, {bad_descriptor, Value}})
+ end,
+ {0, sock_opt(ip_comm, Addr, [{fd, Fd} | BaseOpts]), IpFamily};
+ error ->
+ {Port, sock_opt(ip_comm, Addr, BaseOpts), IpFamilyDefault}
+ end.
+
+
+fd_of(FdStr) ->
+ case (catch list_to_integer(FdStr)) of
+ Fd when is_integer(Fd) ->
+ Fd;
+ _ ->
+ throw({error, {bad_descriptor, FdStr}})
+ end.
+
+ip_family_of(IpFamilyStr) ->
+ IpFamily = list_to_atom(IpFamilyStr),
+ case lists:member(IpFamily, [inet, inet6, inet6fb4]) of
+ true ->
+ IpFamily;
+ false ->
+ throw({error, {bad_ipfamily, IpFamilyStr}})
+ end.
+
+
+%%-------------------------------------------------------------------------
+%% 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:transport_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, Opts) ->
+ sock_opt2([{ip, Addr} | Opts]);
+sock_opt(ip_comm, undefined, Opts) ->
+ sock_opt2(Opts);
+sock_opt(_, any = _Addr, Opts) ->
+ sock_opt2(Opts);
+sock_opt(_, undefined = _Addr, Opts) ->
+ sock_opt2(Opts);
+sock_opt(_, {_,_,_,_} = Addr, Opts) ->
+ sock_opt2([{ip, Addr} | Opts]);
+sock_opt(ip_comm, Addr, Opts) ->
+ sock_opt2([{ip, Addr} | Opts]);
+sock_opt(_, Addr, Opts) ->
+ sock_opt2([{ip, Addr} | Opts]).
+
+sock_opt2(Opts) ->
+ [{packet, 0}, {active, false} | Opts].
+
+negotiate(ip_comm,_,_) ->
+ ok;
+negotiate({ssl,_},Socket,Timeout) ->
+ negotiate(Socket, Timeout);
+negotiate({erl_ssl, _}, Socket, Timeout) ->
+ negotiate(Socket, Timeout).
+
+negotiate(Socket, Timeout) ->
+ case ssl:ssl_accept(Socket, Timeout) of
+ ok ->
+ ok;
+ {error, Error} ->
+ case lists:member(Error,
+ [timeout,econnreset,esslaccept,esslerrssl]) of
+ true ->
+ {error,normal};
+ false ->
+ {error, Error}
+ end
+ end.