From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- lib/inets/src/http_lib/http_transport.erl | 353 ++++++++++++++++++++++++++++++ 1 file changed, 353 insertions(+) create mode 100644 lib/inets/src/http_lib/http_transport.erl (limited to 'lib/inets/src/http_lib/http_transport.erl') 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. -- cgit v1.2.3