1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(http_transport).
22
23% Internal application API
24-export([
25	 start/1,
26	 connect/3, connect/4,
27	 listen/4, listen/5,
28	 accept/2, accept/3,
29	 close/2,
30	 send/3,
31	 controlling_process/3,
32	 setopts/3, getopts/2, getopts/3,
33	 getstat/2,
34	 peername/2, sockname/2,
35	 resolve/0
36	]).
37-export([negotiate/3]).
38-export([ipv4_name/1, ipv6_name/1]).
39
40-include_lib("inets/src/inets_app/inets_internal.hrl").
41-include("http_internal.hrl").
42
43
44%%%=========================================================================
45%%%  Internal application API
46%%%=========================================================================
47
48%%-------------------------------------------------------------------------
49%% start(SocketType) -> ok | {error, Reason}
50%%      SocketType = ip_comm | {ssl, _}
51%%
52%% Description: Makes sure ssl is started.
53%%-------------------------------------------------------------------------
54start(ip_comm) ->
55    ok;
56start({ip_comm, _}) ->
57    ok;
58start({ssl, _}) ->
59    do_start_ssl();
60start({essl, _}) ->
61    do_start_ssl().
62
63do_start_ssl() ->
64    try lists:foreach(fun(App) ->
65			      ok = application:ensure_started(App)
66		      end,
67		      [crypto, asn1, public_key, ssl])
68    catch
69	_:Reason ->
70	    {error, Reason}
71    end.
72
73
74%%-------------------------------------------------------------------------
75%% connect(SocketType, Address, Options, Timeout) ->
76%%                                            {ok, Socket} | {error, Reason}
77%%      SocketType = ip_comm | {ssl, SslConfig}
78%%      Address = {Host, Port}
79%%      Options = [option()]
80%%      Socket = socket()
81%%      option() = ipfamily() | {ip, ip_address()} | {port, integer()}
82%%      ipfamily() = inet | inet6
83%%
84%% Description: Connects to the Host and Port specified in HTTPRequest.
85%%-------------------------------------------------------------------------
86
87connect(SocketType, Address, Opts) ->
88    connect(SocketType, Address, Opts, infinity).
89connect(ip_comm, {Host, Port}, Opts0, Timeout) ->
90    Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0 ],
91    try gen_tcp:connect(Host, Port, Opts, Timeout) of
92	{ok, _} = OK ->
93	    OK;
94	{error, _} = ERROR ->
95	    ERROR
96    catch
97	exit:{badarg, _} ->
98	    {error, {eoptions, Opts}};
99	exit:badarg ->
100	    {error, {eoptions, Opts}}
101    end;
102
103%% Wrapper for backaward compatibillity
104connect({ssl, SslConfig}, Address, Opts, Timeout) ->
105    connect({?HTTP_DEFAULT_SSL_KIND, SslConfig}, Address, Opts, Timeout);
106
107connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) ->
108    Opts = [binary, {active, false}, {ssl_imp, new} | Opts0] ++ SslConfig,
109    case (catch ssl:connect(Host, Port, Opts, Timeout)) of
110	{'EXIT', Reason} ->
111	    {error, {eoptions, Reason}};
112	{ok, _} = OK ->
113	    OK;
114	{error, _} = ERROR ->
115	    ERROR
116    end.
117
118
119%%-------------------------------------------------------------------------
120%% listen(SocketType, Addr, Port, Fd) -> {ok, Socket} | {error, Reason}
121%%      SocketType = ip_comm | {ssl, SSLConfig}
122%%      Port = integer()
123%%      Socket = socket()
124%%      Fd = undefined | fd()
125%%
126%% Description: Sets up socket to listen on the port Port on the local
127%% host using either gen_tcp or ssl. In the gen_tcp case the port
128%% might allready have been initiated by a wrapper-program and is
129%% given as an Fd that can be retrieved by init:get_argument. The
130%% reason for this to enable a HTTP-server not running as root to use
131%% port 80.
132%%-------------------------------------------------------------------------
133listen(ip_comm, Addr, Port, Fd, IpFamily) ->
134    listen_ip_comm(Addr, Port, [], Fd, IpFamily);
135
136listen({ip_comm, SockOpts}, Addr, Port, Fd, IpFamily) ->
137    listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily);
138
139listen({essl, SSLConfig}, Addr, Port, Fd, IpFamily) ->
140    listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily, []).
141
142listen(ip_comm, Addr, Port, IpFamily) ->
143    listen_ip_comm(Addr, Port, [], undefined, IpFamily);
144
145%% Wrapper for backaward compatibillity
146listen({ssl, SSLConfig}, Addr, Port, IpFamily) ->
147    listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port, IpFamily);
148
149listen({essl, SSLConfig}, Addr, Port, IpFamily) ->
150    {SSLConfig2, ExtraOpts} = case proplists:get_value(log_alert, SSLConfig, undefined) of
151		    undefined ->
152			{SSLConfig, []};
153		    LogAlert ->
154			{proplists:delete(log_alert, SSLConfig), [{log_alert, LogAlert}]}
155		end,
156    listen_ssl(Addr, Port, undefined, SSLConfig2, IpFamily, ExtraOpts).
157
158listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily) ->
159    case (catch do_listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily)) of
160	{'EXIT', Reason} ->
161	    {error, {exit, Reason}};
162	Else ->
163	    Else
164    end.
165
166do_listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily) ->
167    Backlog = proplists:get_value(backlog, SockOpts, 128),
168    {NewPort, Opts} = get_socket_info(Addr, Port, Fd,
169				      [{backlog, Backlog}, {reuseaddr, true} | SockOpts]),
170    Opts2 = [IpFamily | Opts],
171    gen_tcp:listen(NewPort, Opts2).
172
173listen_ssl(Addr, Port, Fd, Opts0, IpFamily, ExtraOpts) ->
174    Backlog = proplists:get_value(backlog, Opts0, 128),
175    {NewPort, SockOpt} = get_socket_info(Addr, Port, Fd,
176					 [{backlog, Backlog}, {reuseaddr, true}]),
177    Opts = SockOpt ++ Opts0,
178    Opts2 = [IpFamily | Opts],
179    ssl:listen(NewPort, Opts2 ++ ExtraOpts).
180
181get_socket_info(Addr, Port, Fd, BaseOpts) ->
182    %% The presence of a file descriptor takes precedence
183    case Fd of
184	undefined ->
185	    {Port, sock_opts(Addr, BaseOpts)};
186	Fd ->
187	    {0, sock_opts([{fd, Fd} | BaseOpts])}
188    end.
189
190%%-------------------------------------------------------------------------
191%% accept(SocketType, ListenSocket) -> {ok, Socket} | {error, Reason}
192%% accept(SocketType, ListenSocket, Timeout) -> ok | {error, Reason}
193%%   SocketType = ip_comm | {ssl, SSLConfig}
194%%   ListenSocket = socket()
195%%   Timeout = infinity | integer() >= 0
196%%   Socket = socket()
197%%
198%% Description: Accepts an incoming connection request on a listen socket,
199%% using either gen_tcp or ssl.
200%%-------------------------------------------------------------------------
201accept(SocketType, ListenSocket) ->
202    accept(SocketType, ListenSocket, infinity).
203
204accept(ip_comm, ListenSocket, Timeout) ->
205    gen_tcp:accept(ListenSocket, Timeout);
206accept({ip_comm, _}, ListenSocket, Timeout) ->
207    gen_tcp:accept(ListenSocket, Timeout);
208
209%% Wrapper for backaward compatibillity
210accept({ssl, SSLConfig}, ListenSocket, Timeout) ->
211    accept({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, ListenSocket, Timeout);
212
213accept({essl, _SSLConfig}, ListenSocket, Timeout) ->
214    ssl:transport_accept(ListenSocket, Timeout).
215
216
217%%-------------------------------------------------------------------------
218%% controlling_process(SocketType, Socket, NewOwner) -> ok | {error, Reason}
219%%   SocketType = ip_comm | {ssl, _}
220%%   Socket = socket()
221%%   NewOwner = pid()
222%%
223%% Description: Assigns a new controlling process to Socket.
224%%-------------------------------------------------------------------------
225controlling_process(ip_comm, Socket, NewOwner) ->
226    gen_tcp:controlling_process(Socket, NewOwner);
227controlling_process({ip_comm, _}, Socket, NewOwner) ->
228    gen_tcp:controlling_process(Socket, NewOwner);
229
230%% Wrapper for backaward compatibillity
231controlling_process({ssl, SSLConfig}, Socket, NewOwner) ->
232    controlling_process({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, NewOwner);
233
234controlling_process({essl, _}, Socket, NewOwner) ->
235    ssl:controlling_process(Socket, NewOwner).
236
237
238%%-------------------------------------------------------------------------
239%% setopts(SocketType, Socket, Options) -> ok | {error, Reason}
240%%     SocketType = ip_comm | {ssl, _}
241%%     Socket = socket()
242%%     Options = list()
243%% Description: Sets one or more options for a socket, using either
244%% gen_tcp or ssl.
245%%-------------------------------------------------------------------------
246setopts(ip_comm, Socket, Options) ->
247    inet:setopts(Socket, Options);
248setopts({ip_comm, _}, Socket, Options) ->
249    inet:setopts(Socket, Options);
250
251%% Wrapper for backaward compatibillity
252setopts({ssl, SSLConfig}, Socket, Options) ->
253    setopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options);
254
255setopts({essl, _}, Socket, Options) ->
256    (catch ssl:setopts(Socket, Options)).
257
258
259%%-------------------------------------------------------------------------
260%% getopts(SocketType, Socket [, Opts]) -> ok | {error, Reason}
261%%     SocketType = ip_comm | {ssl, _}
262%%     Socket = socket()
263%%     Opts   = socket_options()
264%% Description: Gets the values for some options.
265%%-------------------------------------------------------------------------
266getopts(SocketType, Socket) ->
267    Opts = [packet, packet_size, recbuf, sndbuf, priority, tos, send_timeout],
268    getopts(SocketType, Socket, Opts).
269
270getopts({ip_comm, _}, Socket, Options) ->
271    getopts(ip_comm, Socket, Options);
272
273getopts(ip_comm, Socket, Options) ->
274    case inet:getopts(Socket, Options) of
275	{ok, SocketOpts} ->
276	    SocketOpts;
277	{error, _} ->
278	    []
279    end;
280
281%% Wrapper for backaward compatibillity
282getopts({ssl, SSLConfig}, Socket, Options) ->
283    getopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options);
284
285getopts({essl, _}, Socket, Options) ->
286    getopts_ssl(Socket, Options).
287
288getopts_ssl(Socket, Options) ->
289    case ssl:getopts(Socket, Options) of
290	{ok, SocketOpts} ->
291	    SocketOpts;
292	{error, _} ->
293	    []
294    end.
295
296
297%%-------------------------------------------------------------------------
298%% getstat(SocketType, Socket) -> socket_stats()
299%%     SocketType = ip_comm | {ssl, _}
300%%     Socket = socket()
301%%     socket_stats() = list()
302%% Description: Gets the socket stats values for the socket
303%%-------------------------------------------------------------------------
304getstat(ip_comm = _SocketType, Socket) ->
305    case inet:getstat(Socket) of
306	{ok, Stats} ->
307	    Stats;
308	{error, _} ->
309	    []
310    end;
311
312%% Wrapper for backaward compatibillity
313getstat({ssl, SSLConfig}, Socket) ->
314    getstat({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
315
316getstat({essl, _} = _SocketType, _Socket) ->
317    [].
318
319
320%%-------------------------------------------------------------------------
321%% send(RequestOrSocketType, Socket, Message) -> ok | {error, Reason}
322%%     SocketType = ip_comm | {ssl, _}
323%%     Socket = socket()
324%%     Message = list() | binary()
325%% Description: Sends a packet on a socket, using either gen_tcp or ssl.
326%%-------------------------------------------------------------------------
327send(ip_comm, Socket, Message) ->
328    gen_tcp:send(Socket, Message);
329send({ip_comm, _}, Socket, Message) ->
330    gen_tcp:send(Socket, Message);
331
332%% Wrapper for backaward compatibillity
333send({ssl, SSLConfig}, Socket, Message) ->
334    send({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Message);
335
336send({essl, _}, Socket, Message) ->
337    ssl:send(Socket, Message).
338
339%%-------------------------------------------------------------------------
340%% close(SocketType, Socket) -> ok | {error, Reason}
341%%     SocketType = ip_comm | {ssl, _}
342%%     Socket = socket()
343%%
344%% Description: Closes a socket, using either gen_tcp or ssl.
345%%-------------------------------------------------------------------------
346close(ip_comm, Socket) ->
347    gen_tcp:close(Socket);
348close({ip_comm, []}, Socket) ->
349    gen_tcp:close(Socket);
350
351%% Wrapper for backaward compatibillity
352close({ssl, SSLConfig}, Socket) ->
353    close({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
354
355close({essl, _}, Socket) ->
356    ssl:close(Socket).
357
358
359%%-------------------------------------------------------------------------
360%% peername(SocketType, Socket) -> {Port, SockName}
361%%     SocketType = ip_comm | {ssl, _}
362%%     Socket = socket()
363%%     Port = integer()  (-1 if error occured)
364%%     PeerName = string()
365%%
366%% Description: Returns the address and port for the other end of a
367%% connection, usning either gen_tcp or ssl.
368%%-------------------------------------------------------------------------
369peername(ip_comm, Socket) ->
370    do_peername(inet:peername(Socket));
371peername({ip_comm, _}, Socket) ->
372    do_peername(inet:peername(Socket));
373
374%% Wrapper for backaward compatibillity
375peername({ssl, SSLConfig}, Socket) ->
376    peername({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
377
378peername({essl, _}, Socket) ->
379    do_peername(ssl:peername(Socket)).
380
381do_peername({ok, {Addr, Port}})
382  when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
383    PeerName = ipv4_name(Addr),
384    {Port, PeerName};
385do_peername({ok, {Addr, Port}})
386  when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
387    PeerName = ipv6_name(Addr),
388    {Port, PeerName};
389do_peername({error, _}) ->
390    {-1, "unknown"}.
391
392
393%%-------------------------------------------------------------------------
394%% sockname(SocketType, Socket) -> {Port, SockName}
395%%     SocketType = ip_comm | {ssl, _}
396%%     Socket = socket()
397%%     Port = integer()  (-1 if error occured)
398%%     SockName = string()
399%%
400%% Description: Returns the address and port for the local (our) end
401%% other end of connection, using either gen_tcp or ssl.
402%%-------------------------------------------------------------------------
403sockname(ip_comm, Socket) ->
404    do_sockname(inet:sockname(Socket));
405sockname({ip_comm, _}, Socket) ->
406    do_sockname(inet:sockname(Socket));
407%% Wrapper for backaward compatibillity
408sockname({ssl, SSLConfig}, Socket) ->
409    sockname({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
410
411sockname({essl, _}, Socket) ->
412    do_sockname(ssl:sockname(Socket)).
413
414do_sockname({ok, {Addr, Port}})
415  when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
416    SockName = ipv4_name(Addr),
417    {Port, SockName};
418do_sockname({ok, {Addr, Port}})
419  when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
420    SockName = ipv6_name(Addr),
421    {Port, SockName};
422do_sockname({error, _}) ->
423    {-1, "unknown"}.
424
425
426%%-------------------------------------------------------------------------
427%% resolve() -> HostName
428%%     HostName = string()
429%%
430%% Description: Returns the local hostname.
431%%-------------------------------------------------------------------------
432resolve() ->
433    {ok, Name} = inet:gethostname(),
434    Name.
435
436
437%%-------------------------------------------------------------------------
438%% ipv4_name(Ipv4Addr) -> string()
439%% ipv6_name(Ipv6Addr) -> string()
440%%     Ipv4Addr = ip4_address()
441%%     Ipv6Addr = ip6_address()
442%%
443%% Description: Returns the local hostname.
444%%-------------------------------------------------------------------------
445ipv4_name({A, B, C, D}) ->
446    integer_to_list(A) ++ "." ++
447	integer_to_list(B) ++ "." ++
448	integer_to_list(C) ++ "." ++
449	integer_to_list(D).
450
451ipv6_name({A, B, C, D, E, F, G, H}) ->
452    http_util:integer_to_hexlist(A) ++ ":"++
453	http_util:integer_to_hexlist(B) ++ ":" ++
454	http_util:integer_to_hexlist(C) ++ ":" ++
455	http_util:integer_to_hexlist(D) ++ ":" ++
456	http_util:integer_to_hexlist(E) ++ ":" ++
457	http_util:integer_to_hexlist(F) ++ ":" ++
458	http_util:integer_to_hexlist(G) ++ ":" ++
459	http_util:integer_to_hexlist(H).
460
461
462%%%========================================================================
463%%% Internal functions
464%%%========================================================================
465
466%% -- sock_opts --
467%% Address any comes from directive: BindAddress "*"
468sock_opts(undefined, Opts) ->
469    sock_opts(Opts);
470sock_opts(any = Addr, Opts) ->
471    sock_opts([{ip, Addr} | Opts]);
472sock_opts(Addr, Opts) ->
473    sock_opts([{ip, Addr} | Opts]).
474
475sock_opts(Opts) ->
476    [{packet, 0}, {active, false} | Opts].
477
478
479%% -- negotiate --
480negotiate(ip_comm,_,_) ->
481    ok;
482negotiate({ip_comm, _},_,_) ->
483    ok;
484negotiate({ssl, SSLConfig}, Socket, Timeout) ->
485    negotiate({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Timeout);
486negotiate({essl, _}, Socket, Timeout) ->
487    negotiate_ssl(Socket, Timeout).
488
489negotiate_ssl(Socket, Timeout) ->
490    ssl:handshake(Socket, Timeout).
491