/lib/inets/src/http_lib/http_transport.erl
Erlang | 490 lines | 254 code | 74 blank | 162 comment | 0 complexity | 6fb6ba9ce14a9ce8f720796be05e01cd MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0
- %%
- %% %CopyrightBegin%
- %%
- %% Copyright Ericsson AB 2004-2016. All Rights Reserved.
- %%
- %% Licensed under the Apache License, Version 2.0 (the "License");
- %% you may not use this file except in compliance with the License.
- %% You may obtain a copy of the License at
- %%
- %% http://www.apache.org/licenses/LICENSE-2.0
- %%
- %% Unless required by applicable law or agreed to in writing, software
- %% distributed under the License is distributed on an "AS IS" BASIS,
- %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- %% See the License for the specific language governing permissions and
- %% limitations under the License.
- %%
- %% %CopyrightEnd%
- %%
- -module(http_transport).
- % Internal application API
- -export([
- start/1,
- connect/3, connect/4,
- listen/4, listen/5,
- accept/2, accept/3,
- close/2,
- send/3,
- controlling_process/3,
- setopts/3, getopts/2, getopts/3,
- getstat/2,
- peername/2, sockname/2,
- resolve/0
- ]).
- -export([negotiate/3]).
- -export([ipv4_name/1, ipv6_name/1]).
- -include_lib("inets/src/inets_app/inets_internal.hrl").
- -include("http_internal.hrl").
- %%%=========================================================================
- %%% Internal application API
- %%%=========================================================================
- %%-------------------------------------------------------------------------
- %% start(SocketType) -> ok | {error, Reason}
- %% SocketType = ip_comm | {ssl, _}
- %%
- %% Description: Makes sure ssl is started.
- %%-------------------------------------------------------------------------
- start(ip_comm) ->
- ok;
- start({ip_comm, _}) ->
- ok;
- start({ssl, _}) ->
- do_start_ssl();
- start({essl, _}) ->
- do_start_ssl().
- do_start_ssl() ->
- try lists:foreach(fun(App) ->
- ok = application:ensure_started(App)
- end,
- [crypto, asn1, public_key, ssl])
- catch
- _:Reason ->
- {error, Reason}
- 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, {Host, Port}, Opts0, Timeout) ->
- Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0 ],
- try gen_tcp:connect(Host, Port, Opts, Timeout) of
- {ok, _} = OK ->
- OK;
- {error, _} = ERROR ->
- ERROR
- catch
- exit:{badarg, _} ->
- {error, {eoptions, Opts}};
- exit:badarg ->
- {error, {eoptions, Opts}}
- end;
- %% Wrapper for backaward compatibillity
- connect({ssl, SslConfig}, Address, Opts, Timeout) ->
- connect({?HTTP_DEFAULT_SSL_KIND, SslConfig}, Address, Opts, Timeout);
- connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) ->
- Opts = [binary, {active, false}, {ssl_imp, new} | Opts0] ++ SslConfig,
- case (catch ssl:connect(Host, Port, Opts, Timeout)) of
- {'EXIT', Reason} ->
- {error, {eoptions, Reason}};
- {ok, _} = OK ->
- OK;
- {error, _} = ERROR ->
- ERROR
- end.
- %%-------------------------------------------------------------------------
- %% listen(SocketType, Addr, Port, Fd) -> {ok, Socket} | {error, Reason}
- %% SocketType = ip_comm | {ssl, SSLConfig}
- %% Port = integer()
- %% Socket = socket()
- %% Fd = undefined | fd()
- %%
- %% 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(ip_comm, Addr, Port, Fd, IpFamily) ->
- listen_ip_comm(Addr, Port, [], Fd, IpFamily);
- listen({ip_comm, SockOpts}, Addr, Port, Fd, IpFamily) ->
- listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily);
- listen({essl, SSLConfig}, Addr, Port, Fd, IpFamily) ->
- listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily, []).
- listen(ip_comm, Addr, Port, IpFamily) ->
- listen_ip_comm(Addr, Port, [], undefined, IpFamily);
- %% Wrapper for backaward compatibillity
- listen({ssl, SSLConfig}, Addr, Port, IpFamily) ->
- listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port, IpFamily);
- listen({essl, SSLConfig}, Addr, Port, IpFamily) ->
- {SSLConfig2, ExtraOpts} = case proplists:get_value(log_alert, SSLConfig, undefined) of
- undefined ->
- {SSLConfig, []};
- LogAlert ->
- {proplists:delete(log_alert, SSLConfig), [{log_alert, LogAlert}]}
- end,
- listen_ssl(Addr, Port, undefined, SSLConfig2, IpFamily, ExtraOpts).
- listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily) ->
- case (catch do_listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily)) of
- {'EXIT', Reason} ->
- {error, {exit, Reason}};
- Else ->
- Else
- end.
- do_listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily) ->
- Backlog = proplists:get_value(backlog, SockOpts, 128),
- {NewPort, Opts} = get_socket_info(Addr, Port, Fd,
- [{backlog, Backlog}, {reuseaddr, true} | SockOpts]),
- Opts2 = [IpFamily | Opts],
- gen_tcp:listen(NewPort, Opts2).
- listen_ssl(Addr, Port, Fd, Opts0, IpFamily, ExtraOpts) ->
- Backlog = proplists:get_value(backlog, Opts0, 128),
- {NewPort, SockOpt} = get_socket_info(Addr, Port, Fd,
- [{backlog, Backlog}, {reuseaddr, true}]),
- Opts = SockOpt ++ Opts0,
- Opts2 = [IpFamily | Opts],
- ssl:listen(NewPort, Opts2 ++ ExtraOpts).
- get_socket_info(Addr, Port, Fd, BaseOpts) ->
- %% The presence of a file descriptor takes precedence
- case Fd of
- undefined ->
- {Port, sock_opts(Addr, BaseOpts)};
- Fd ->
- {0, sock_opts([{fd, Fd} | BaseOpts])}
- 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({ip_comm, _}, ListenSocket, Timeout) ->
- gen_tcp:accept(ListenSocket, Timeout);
- %% Wrapper for backaward compatibillity
- accept({ssl, SSLConfig}, ListenSocket, Timeout) ->
- accept({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, ListenSocket, Timeout);
- accept({essl, _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({ip_comm, _}, Socket, NewOwner) ->
- gen_tcp:controlling_process(Socket, NewOwner);
- %% Wrapper for backaward compatibillity
- controlling_process({ssl, SSLConfig}, Socket, NewOwner) ->
- controlling_process({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, NewOwner);
- controlling_process({essl, _}, 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({ip_comm, _}, Socket, Options) ->
- inet:setopts(Socket, Options);
- %% Wrapper for backaward compatibillity
- setopts({ssl, SSLConfig}, Socket, Options) ->
- setopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options);
- setopts({essl, _}, Socket, Options) ->
- (catch ssl:setopts(Socket, Options)).
- %%-------------------------------------------------------------------------
- %% getopts(SocketType, Socket [, Opts]) -> ok | {error, Reason}
- %% SocketType = ip_comm | {ssl, _}
- %% Socket = socket()
- %% Opts = socket_options()
- %% Description: Gets the values for some options.
- %%-------------------------------------------------------------------------
- getopts(SocketType, Socket) ->
- Opts = [packet, packet_size, recbuf, sndbuf, priority, tos, send_timeout],
- getopts(SocketType, Socket, Opts).
- getopts({ip_comm, _}, Socket, Options) ->
- getopts(ip_comm, Socket, Options);
- getopts(ip_comm, Socket, Options) ->
- case inet:getopts(Socket, Options) of
- {ok, SocketOpts} ->
- SocketOpts;
- {error, _} ->
- []
- end;
- %% Wrapper for backaward compatibillity
- getopts({ssl, SSLConfig}, Socket, Options) ->
- getopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options);
- getopts({essl, _}, Socket, Options) ->
- getopts_ssl(Socket, Options).
- getopts_ssl(Socket, Options) ->
- case ssl:getopts(Socket, Options) of
- {ok, SocketOpts} ->
- SocketOpts;
- {error, _} ->
- []
- end.
-
- %%-------------------------------------------------------------------------
- %% getstat(SocketType, Socket) -> socket_stats()
- %% SocketType = ip_comm | {ssl, _}
- %% Socket = socket()
- %% socket_stats() = list()
- %% Description: Gets the socket stats values for the socket
- %%-------------------------------------------------------------------------
- getstat(ip_comm = _SocketType, Socket) ->
- case inet:getstat(Socket) of
- {ok, Stats} ->
- Stats;
- {error, _} ->
- []
- end;
- %% Wrapper for backaward compatibillity
- getstat({ssl, SSLConfig}, Socket) ->
- getstat({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
- getstat({essl, _} = _SocketType, _Socket) ->
- [].
- %%-------------------------------------------------------------------------
- %% 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({ip_comm, _}, Socket, Message) ->
- gen_tcp:send(Socket, Message);
- %% Wrapper for backaward compatibillity
- send({ssl, SSLConfig}, Socket, Message) ->
- send({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Message);
- send({essl, _}, 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({ip_comm, []}, Socket) ->
- gen_tcp:close(Socket);
- %% Wrapper for backaward compatibillity
- close({ssl, SSLConfig}, Socket) ->
- close({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
- close({essl, _}, Socket) ->
- ssl:close(Socket).
- %%-------------------------------------------------------------------------
- %% peername(SocketType, Socket) -> {Port, SockName}
- %% SocketType = ip_comm | {ssl, _}
- %% Socket = socket()
- %% Port = integer() (-1 if error occured)
- %% PeerName = string()
- %%
- %% Description: Returns the address and port for the other end of a
- %% connection, usning either gen_tcp or ssl.
- %%-------------------------------------------------------------------------
- peername(ip_comm, Socket) ->
- do_peername(inet:peername(Socket));
- peername({ip_comm, _}, Socket) ->
- do_peername(inet:peername(Socket));
- %% Wrapper for backaward compatibillity
- peername({ssl, SSLConfig}, Socket) ->
- peername({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
- peername({essl, _}, Socket) ->
- do_peername(ssl:peername(Socket)).
- do_peername({ok, {Addr, Port}})
- when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
- PeerName = ipv4_name(Addr),
- {Port, PeerName};
- do_peername({ok, {Addr, Port}})
- when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
- PeerName = ipv6_name(Addr),
- {Port, PeerName};
- do_peername({error, _}) ->
- {-1, "unknown"}.
- %%-------------------------------------------------------------------------
- %% sockname(SocketType, Socket) -> {Port, SockName}
- %% SocketType = ip_comm | {ssl, _}
- %% Socket = socket()
- %% Port = integer() (-1 if error occured)
- %% SockName = string()
- %%
- %% Description: Returns the address and port for the local (our) end
- %% other end of connection, using either gen_tcp or ssl.
- %%-------------------------------------------------------------------------
- sockname(ip_comm, Socket) ->
- do_sockname(inet:sockname(Socket));
- sockname({ip_comm, _}, Socket) ->
- do_sockname(inet:sockname(Socket));
- %% Wrapper for backaward compatibillity
- sockname({ssl, SSLConfig}, Socket) ->
- sockname({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
- sockname({essl, _}, Socket) ->
- do_sockname(ssl:sockname(Socket)).
- do_sockname({ok, {Addr, Port}})
- when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
- SockName = ipv4_name(Addr),
- {Port, SockName};
- do_sockname({ok, {Addr, Port}})
- when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
- SockName = ipv6_name(Addr),
- {Port, SockName};
- do_sockname({error, _}) ->
- {-1, "unknown"}.
- %%-------------------------------------------------------------------------
- %% resolve() -> HostName
- %% HostName = string()
- %%
- %% Description: Returns the local hostname.
- %%-------------------------------------------------------------------------
- resolve() ->
- {ok, Name} = inet:gethostname(),
- Name.
- %%-------------------------------------------------------------------------
- %% ipv4_name(Ipv4Addr) -> string()
- %% ipv6_name(Ipv6Addr) -> string()
- %% Ipv4Addr = ip4_address()
- %% Ipv6Addr = ip6_address()
- %%
- %% Description: Returns the local hostname.
- %%-------------------------------------------------------------------------
- ipv4_name({A, B, C, D}) ->
- integer_to_list(A) ++ "." ++
- integer_to_list(B) ++ "." ++
- integer_to_list(C) ++ "." ++
- integer_to_list(D).
- ipv6_name({A, B, C, D, E, F, G, H}) ->
- 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).
- %%%========================================================================
- %%% Internal functions
- %%%========================================================================
- %% -- sock_opts --
- %% Address any comes from directive: BindAddress "*"
- sock_opts(undefined, Opts) ->
- sock_opts(Opts);
- sock_opts(any = Addr, Opts) ->
- sock_opts([{ip, Addr} | Opts]);
- sock_opts(Addr, Opts) ->
- sock_opts([{ip, Addr} | Opts]).
- sock_opts(Opts) ->
- [{packet, 0}, {active, false} | Opts].
- %% -- negotiate --
- negotiate(ip_comm,_,_) ->
- ok;
- negotiate({ip_comm, _},_,_) ->
- ok;
- negotiate({ssl, SSLConfig}, Socket, Timeout) ->
- negotiate({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Timeout);
- negotiate({essl, _}, Socket, Timeout) ->
- negotiate_ssl(Socket, Timeout).
- negotiate_ssl(Socket, Timeout) ->
- ssl:ssl_accept(Socket, Timeout).