reverse_protocol.erl 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. %% Feel free to use, reuse and abuse the code in this file.
  2. -module(reverse_protocol).
  3. -behaviour(gen_statem).
  4. -behaviour(ranch_protocol).
  5. %% API.
  6. -export([start_link/3]).
  7. %% gen_statem.
  8. -export([callback_mode/0]).
  9. -export([init/1]).
  10. -export([connected/3]).
  11. -export([terminate/3]).
  12. -export([code_change/4]).
  13. -define(TIMEOUT, 60000).
  14. -record(state, {socket, transport}).
  15. %% API.
  16. start_link(Ref, Transport, Opts) ->
  17. {ok, proc_lib:spawn_link(?MODULE, init, [{Ref, Transport, Opts}])}.
  18. %% gen_statem.
  19. callback_mode() ->
  20. state_functions.
  21. init({Ref, Transport, _Opts = []}) ->
  22. {ok, Socket} = ranch:handshake(Ref),
  23. ok = Transport:setopts(Socket, [{active, once}, {packet, line}]),
  24. gen_statem:enter_loop(?MODULE, [], connected,
  25. #state{socket=Socket, transport=Transport},
  26. [?TIMEOUT]).
  27. connected(info, {tcp, Socket, Data}, _StateData=#state{
  28. socket=Socket, transport=Transport})
  29. when byte_size(Data) > 1 ->
  30. Transport:setopts(Socket, [{active, once}]),
  31. Transport:send(Socket, reverse_binary(Data)),
  32. {keep_state_and_data, ?TIMEOUT};
  33. connected(info, {tcp_closed, _Socket}, _StateData) ->
  34. {stop, normal};
  35. connected(info, {tcp_error, _, Reason}, _StateData) ->
  36. {stop, Reason};
  37. connected({call, From}, _Request, _StateData) ->
  38. gen_statem:reply(From, ok),
  39. keep_state_and_data;
  40. connected(cast, _Msg, _StateData) ->
  41. keep_state_and_data;
  42. connected(timeout, _Msg, _StateData) ->
  43. {stop, normal};
  44. connected(_EventType, _Msg, _StateData) ->
  45. {stop, normal}.
  46. terminate(Reason, StateName, StateData=#state{
  47. socket=Socket, transport=Transport})
  48. when Socket=/=undefined andalso Transport=/=undefined ->
  49. catch Transport:close(Socket),
  50. terminate(Reason, StateName,
  51. StateData#state{socket=undefined, transport=undefined});
  52. terminate(_Reason, _StateName, _StateData) ->
  53. ok.
  54. code_change(_OldVsn, StateName, StateData, _Extra) ->
  55. {ok, StateName, StateData}.
  56. %% Internal.
  57. reverse_binary(B) when is_binary(B) ->
  58. [list_to_binary(lists:reverse(binary_to_list(
  59. binary:part(B, {0, byte_size(B)-2})
  60. ))), "\r\n"].