Skip to content

Commit

Permalink
To Cowboy 2.2.0! (2600hz#4438)
Browse files Browse the repository at this point in the history
* move to tagged version of cowboy

* move function call to new module

* add xref'd updates

* last of the xref complaints

* remove deprecated module

* remove unused field

* update to use maps for req/resp headers

* more updates to new function signatures

* mostly resp header updates

* as far as dialyzer can take us, for now

* more dialyzer saves

* ignore ci dep

* convert to cowboy 2 for media proxying

* convert to cowboy 2 way of life

* last of the initial blackhole changes

* update fax to use cowboy 2 stuff

* more function updates

* add cast functions

* cleanup a bit

* remove signup from swagger

* remove gen_smtp from crossbar

* work through getting requests to work

* update spec

* convert headers to maps

* normalize headers to to_lower

* update blackhole's websocket handler and listener(s)

* change logging

* add contraints

* update media handlers

* handle the request

* run all seq tests

* adjust usage of get_range/2

* clean up app files

* update schemas for blackhole

* patch around required content-type header
  • Loading branch information
jamesaimonetti authored and lazedo committed Jan 3, 2018
1 parent e5bab92 commit 870263c
Show file tree
Hide file tree
Showing 62 changed files with 1,276 additions and 1,611 deletions.
2 changes: 1 addition & 1 deletion applications/blackhole/src/blackhole.app.src
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
[{applications,[cowboy,kazoo,kazoo_amqp,kazoo_apps,kazoo_auth,
kazoo_bindings,kazoo_config,kazoo_documents,
kazoo_modb,kazoo_stdlib,kazoo_token_buckets,
kernel,lager,stdlib]},
kernel,lager,ssl,stdlib]},
{description,"blackhole - websocket"},
{env,[{is_kazoo_app,true}]},
{mod,{blackhole_app,[]}},
Expand Down
1 change: 1 addition & 0 deletions applications/blackhole/src/blackhole.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
-include_lib("kazoo_stdlib/include/kz_databases.hrl").
-include_lib("kazoo_apps/include/kz_hooks.hrl").

-define(APP, 'blackhole').
-define(APP_NAME, <<"blackhole">>).
-define(APP_VERSION, <<"4.0.0">>).
-define(CONFIG_CAT, <<"blackhole">>).
Expand Down
221 changes: 221 additions & 0 deletions applications/blackhole/src/blackhole_init.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2011-2017, 2600Hz INC
%%% @doc
%%%
%%% @end
%%% @contributors
%%% Karl Anderson
%%% James Aimonetti
%%% Jon Blanton
%%%-------------------------------------------------------------------
-module(blackhole_init).

-export([start_link/0]).

-include("blackhole.hrl").

-define(USE_COMPRESSION, kapps_config:get_is_true(?CONFIG_CAT, <<"compress_response_body">>, 'true')).
-define(SOCKET_PORT, kapps_config:get_integer(?APP_NAME, <<"port">>, 5555)).
-define(SOCKET_ACCEPTORS, kapps_config:get_integer(?APP_NAME, <<"acceptors">>, 100)).

-spec blackhole_routes() -> cowboy_router:routes().
blackhole_routes() -> [{'_', paths_list()}].

paths_list() ->
[api_path()].

api_path() ->
{'_', [], 'blackhole_socket_handler', []}.

%%--------------------------------------------------------------------
%% @public
%% @doc Starts the app for inclusion in a supervisor tree
%%--------------------------------------------------------------------
-spec start_link() -> startlink_ret().
start_link() ->
kz_util:put_callid(?DEFAULT_LOG_SYSTEM_ID),

Dispatch = cowboy_router:compile(blackhole_routes()),

maybe_start_plaintext(Dispatch),
maybe_start_ssl(Dispatch),
'ignore'.

%%--------------------------------------------------------------------
%% @private
%% @doc Functions for onrequest and onresponse callbacks
%%--------------------------------------------------------------------
-spec on_request(cowboy_req:req()) -> cowboy_req:req().
on_request(Req) -> Req.

-spec on_response(cowboy:http_status(), cowboy:http_headers(), text(), cowboy_req:req()) ->
cowboy_req:req().
on_response(_Status, _Headers, _Body, Req) -> Req.

-spec maybe_start_plaintext(cowboy_router:dispatch_rules()) -> 'ok'.
maybe_start_plaintext(Dispatch) ->
case kapps_config:get_is_true(?CONFIG_CAT, <<"use_plaintext">>, 'true') of
'false' -> lager:info("plaintext websocket support not enabled");
'true' ->
Port = ?SOCKET_PORT,
ReqTimeout = kapps_config:get_integer(?CONFIG_CAT, <<"request_timeout_ms">>, 10 * ?MILLISECONDS_IN_SECOND),
Workers = ?SOCKET_ACCEPTORS,

%% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
try
IP = get_binding_ip(),
lager:info("trying to bind to address ~s port ~b", [inet:ntoa(IP), Port]),
cowboy:start_clear('blackhole_socket_handler'
,[{'ip', IP}
,{'port', Port}
,{'num_acceptors', Workers}
]
,#{'env' => #{'dispatch' => Dispatch
,'timeout' => ReqTimeout
}
,'onrequest' => fun on_request/1
,'onresponse' => fun on_response/4
,'compress' => ?USE_COMPRESSION
}
)
of
{'ok', _} ->
lager:info("started plaintext WebSocket server");
{'error', {'already_started', _P}} ->
lager:info("already started plaintext WebSocket server at ~p", [_P])
catch
_E:_R ->
lager:warning("crashed starting WEBSOCKET server: ~s: ~p", [_E, _R])
end
end.

-spec get_binding_ip() -> inet:ip_address().
get_binding_ip() ->
IsIPv6Enabled = kz_network_utils:is_ip_family_supported('inet6'),
IsIPv4Enabled = kz_network_utils:is_ip_family_supported('inet'),

DefaultIP = kz_network_utils:default_binding_all_ip(),

IP = kapps_config:get_string(?CONFIG_CAT, <<"ip">>, DefaultIP),

{'ok', DefaultIPAddress} = inet:parse_address(DefaultIP),

case inet:parse_ipv6strict_address(IP) of
{'ok', IPv6} when IsIPv6Enabled -> IPv6;
{'ok', _} ->
lager:warning("address ~s is ipv6, but ipv6 is not supported by the system, enforcing default ip ~s"
,[IP, inet:ntoa(DefaultIPAddress)]
),
DefaultIPAddress;
{'error', 'einval'} ->
case inet:parse_ipv4strict_address(IP) of
{'ok', IPv4} when IsIPv4Enabled -> IPv4;
{'ok', _} when IsIPv6Enabled ->
lager:warning("address ~s is ipv4, but ipv4 is not supported by the system, enforcing default ip ~s"
,[IP, inet:ntoa(DefaultIPAddress)]
),
DefaultIPAddress;
{'ok', _} ->
lager:warning("address ~s is ipv4, but system reports that ipv4 and ipv6 are not supported by the system, enforcing default ip ~s"
,[IP, inet:ntoa(DefaultIPAddress)]
),
DefaultIPAddress;
{'error', 'einval'} ->
lager:warning("address ~s is not a valid ipv6 or ipv4 address, enforcing default ip ~s"
,[IP, inet:ntoa(DefaultIPAddress)]
),
DefaultIPAddress
end
end.

-spec maybe_start_ssl(cowboy_router:dispatch_rules()) -> 'ok'.
maybe_start_ssl(Dispatch) ->
case kapps_config:get_is_true(?CONFIG_CAT, <<"use_ssl">>, 'false') of
'false' -> lager:info("ssl websocket support not enabled");
'true' -> start_ssl(Dispatch)
end.

-spec start_ssl(cowboy_router:dispatch_rules()) -> 'ok'.
start_ssl(Dispatch) ->
try ssl_opts(code:lib_dir(?APP)) of
SSLOpts ->
lager:debug("trying to start SSL WEBSOCKET server"),
_SslStarted = ssl:start(),
lager:debug("starting SSL : ~p", [_SslStarted]),
ReqTimeout = kapps_config:get_integer(?CONFIG_CAT, <<"request_timeout_ms">>, 10 * ?MILLISECONDS_IN_SECOND),
Workers = kapps_config:get_integer(?CONFIG_CAT, <<"ssl_workers">>, 100),

try
IP = get_binding_ip(),
lager:info("trying to bind SSL WEBSOCKET server to address ~s port ~b"
,[inet:ntoa(IP)
,props:get_value('port', SSLOpts)
]
),
cowboy:start_tls('blackhole_socket_handler_ssl'
,[{'ip', IP}
,{'num_acceptors', Workers}
| SSLOpts
]
,#{'env' => #{'dispatch' => Dispatch
,'timeout' => ReqTimeout
}
,'onrequest' => fun on_request/1
,'onresponse' => fun on_response/4
,'compress' => ?USE_COMPRESSION
}
)
of
{'ok', _} ->
lager:info("started SSL WEBSOCKET server on port ~b", [props:get_value('port', SSLOpts)]);
{'error', {'already_started', _P}} ->
lager:info("already started SSL WEBSOCKET server on port ~b at ~p"
,[props:get_value('port', SSLOpts), _P]
)
catch
'throw':{'invalid_file', _File} ->
lager:info("SSL disabled: failed to find ~s", [_File]);
_E:_R ->
lager:warning("crashed starting SSL WEBSOCKET server: ~s: ~p", [_E, _R])
end
catch
'throw':_E ->
lager:warning("failed to start SSL WEBSOCKET server: ~p", [_E])
end.

-spec ssl_opts(list()) -> kz_proplist().
ssl_opts(RootDir) ->
BaseOpts = base_ssl_opts(RootDir),
case kapps_config:get_string(?CONFIG_CAT, <<"ssl_ca_cert">>) of
'undefined' -> BaseOpts;
SSLCACert -> [{'cacertfile', SSLCACert} | BaseOpts]
end.

-spec base_ssl_opts(list()) -> kz_proplist().
base_ssl_opts(RootDir) ->
[{'port', kapps_config:get_integer(?CONFIG_CAT, <<"ssl_port">>, 5556)}
,{'certfile', find_file(kapps_config:get_string(?CONFIG_CAT
,<<"ssl_cert">>
,filename:join([RootDir, <<"priv/ssl/blackhole.crt">>])
), RootDir)}
,{'keyfile', find_file(kapps_config:get_string(?CONFIG_CAT
,<<"ssl_key">>
,filename:join([RootDir, <<"priv/ssl/blackhole.key">>])
), RootDir)}
,{'password', kapps_config:get_string(?CONFIG_CAT, <<"ssl_password">>, <<>>)}
].

-spec find_file(list(), list()) -> list().
find_file(File, Root) ->
case filelib:is_file(File) of
'true' -> File;
'false' ->
FromRoot = filename:join([Root, File]),
lager:info("failed to find file at ~s, trying ~s", [File, FromRoot]),
case filelib:is_file(FromRoot) of
'true' -> FromRoot;
'false' ->
lager:info("failed to find file at ~s", [FromRoot]),
throw({'invalid_file', File})
end
end.
2 changes: 1 addition & 1 deletion applications/blackhole/src/blackhole_socket_callback.erl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

-type cb_return() :: {'ok', bh_context:context()}.

-spec open(pid(), binary(), any()) -> cb_return().
-spec open(pid(), binary(), inet:ip_address()) -> cb_return().
open(Pid, Id, Ipaddr) ->
IPBin = kz_term:to_binary(inet_parse:ntoa(Ipaddr)),
lager:debug("opening socket (~p) ~p, peer: ~p", [Pid, Id, IPBin]),
Expand Down
83 changes: 46 additions & 37 deletions applications/blackhole/src/blackhole_socket_handler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,63 +8,72 @@
%%%-------------------------------------------------------------------
-module(blackhole_socket_handler).

-export([init/3
,websocket_init/3
,websocket_handle/3
,websocket_info/3
,websocket_terminate/3
-export([init/2
,websocket_init/1
,websocket_handle/2
,websocket_info/2
,websocket_terminate/2
,terminate/3
]).

-include("blackhole.hrl").

-spec init({any(), 'http'}, any(), any()) -> tuple().
init({_Any, 'http'}, _Req0, _HandlerOpts) ->
{'upgrade', 'protocol', 'cowboy_websocket'}.
-spec init(cowboy_req:req(), State) ->
{'ok' | 'cowboy_websocket', cowboy_req:req(), State | {inet:ip_address(), ne_binary()}}.
init(Req, HandlerOpts) ->
lager:info("handling socket init"),
case cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req) of
'undefined' ->
{RemoteIP, _} = cowboy_req:peer(Req),
lager:info("no sub protocols defined by remote client ~p", [RemoteIP]),
{'cowboy_websocket', Req, {RemoteIP, session_id(Req)}};
_SubProtocols ->
lager:warning("sub-protocols are not supported at the moment: ~p", [_SubProtocols]),
{'ok', cowboy_req:reply(400, Req), HandlerOpts}
end.

-spec websocket_init(any(), cowboy_req:req(), any()) -> {'ok', cowboy_req:req(), any()}.
websocket_init(_Type, Req, _Opts) ->
{Peer, _} = cowboy_req:peer(Req),
{RemIp, _} = Peer,
-spec terminate(any(), any(), any()) -> 'ok'.
terminate(_Reason, _Req, _State) ->
lager:info("bh socket going down: ~p", [_Reason]).

{'ok', State} = blackhole_socket_callback:open(self(), session_id(Req), RemIp),
{'ok', Req, State}.
-spec websocket_init({inet:ip_address(), ne_binary()}) -> {'ok', bh_context:context()}.
websocket_init({RemoteIP, SessionsId}) ->
lager:info("init from ~p(~p)", [RemoteIP, SessionsId]),
{'ok', _State} = blackhole_socket_callback:open(self(), SessionsId, RemoteIP).

-spec websocket_handle(any(), cowboy_req:req(), A) -> {'ok', cowboy_req:req(), A}.
websocket_handle({'text', Data}, Req, State) ->
-spec websocket_handle(any(), bh_context:context()) ->
{'ok', bh_context:context(), 'hibernate'}.
websocket_handle({'text', Data}, State) ->
Obj = kz_json:decode(Data),
Action = kz_json:get_value(<<"action">>, Obj, <<"noop">>),
Msg = kz_json:delete_key(<<"action">>, Obj),

case blackhole_socket_callback:recv({Action, Msg}, State) of
{'ok', NewState} -> {'ok', Req, NewState};
'error' -> {'ok', Req, State}
{'ok', NewState} -> {'ok', NewState, 'hibernate'};
'error' -> {'ok', State, 'hibernate'}
end;

websocket_handle(_Other, Req, State) ->
websocket_handle(_Other, State) ->
lager:debug("not handling message : ~p", [_Other]),
{'ok', Req, State}.

-spec websocket_info(any(), cowboy_req:req(), A) -> {'ok', cowboy_req:req(), A}.
websocket_info({'$gen_cast', _}, Req, State) ->
{'ok', Req, State};

websocket_info({'send_data', Data}, Req, State) ->
{'reply', {'text', kz_json:encode(Data)}, Req, State};
{'ok', State, 'hibernate'}.

websocket_info(Info, Req, State) ->
-spec websocket_info(any(), bh_context:context()) -> {'ok', bh_context:context()}.
websocket_info({'$gen_cast', _}, State) ->
{'ok', State};
websocket_info({'send_data', Data}, State) ->
{'reply', {'text', kz_json:encode(Data)}, State};
websocket_info(Info, State) ->
lager:info("unhandled websocket info: ~p", [Info]),
{'ok', Req, State}.
{'ok', State}.

-spec websocket_terminate(any(), cowboy_req:req(), bh_context:context()) -> bh_context:context().
websocket_terminate(_Reason, _Req, State) ->
-spec websocket_terminate(any(), bh_context:context()) -> bh_context:context().
websocket_terminate(_Reason, State) ->
blackhole_socket_callback:close(State).

-spec session_id(cowboy_req:req()) -> binary().
session_id(Req) ->
{Peer, _} = cowboy_req:peer(Req),
{Ip, Port} = Peer,
{IP, Port} = cowboy_req:peer(Req),

BinIp = kz_term:to_binary(inet_parse:ntoa(Ip)),
BinPort = kz_term:to_binary(integer_to_list(Port)),
BinIP = kz_term:to_binary(inet_parse:ntoa(IP)),
BinPort = kz_term:to_binary(Port),

<<BinIp/binary, ":", BinPort/binary>>.
<<BinIP/binary, ":", BinPort/binary>>.
13 changes: 1 addition & 12 deletions applications/blackhole/src/blackhole_sup.erl
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,10 @@

-define(SERVER, ?MODULE).

%% Helper macro for declaring children of supervisor
-define(SOCKET_PORT, kapps_config:get_integer(?APP_NAME, <<"port">>, 5555)).
-define(SOCKET_ACCEPTORS, kapps_config:get_integer(?APP_NAME, <<"acceptors">>, 100)).
-define(SOCKET_OPTIONS, [{'port', ?SOCKET_PORT}]).
-define(COWBOY_ROUTER, cowboy_router:compile([{'_', [{"/", 'blackhole_socket_handler', []}]}])).
-define(COWBOY_OPTIONS, [{'env', [{'dispatch', ?COWBOY_ROUTER}]}]).

-define(RANCH_SPEC(Ref),
ranch:child_spec(Ref, ?SOCKET_ACCEPTORS, ranch_tcp, ?SOCKET_OPTIONS, cowboy_protocol, ?COWBOY_OPTIONS)
).
-define(CHILDREN, [?WORKER('blackhole_listener')
,?WORKER('blackhole_tracking')
,?RANCH_SPEC('blackhole_http_listener')
,?WORKER('blackhole_bindings')
,?WORKER('blackhole_init')
]).

%% ===================================================================
Expand Down Expand Up @@ -70,5 +60,4 @@ init([]) ->

SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},


{'ok', {SupFlags, ?CHILDREN}}.
Loading

0 comments on commit 870263c

Please sign in to comment.