stream_handler_h.erl 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. %% This module behaves differently depending on a specific header.
  2. -module(stream_handler_h).
  3. -behavior(cowboy_stream).
  4. -export([init/3]).
  5. -export([data/4]).
  6. -export([info/3]).
  7. -export([terminate/3]).
  8. -export([early_error/5]).
  9. %% For switch_protocol.
  10. -export([takeover/7]).
  11. -record(state, {
  12. pid,
  13. test
  14. }).
  15. init(StreamID, Req, Opts) ->
  16. Pid = list_to_pid(binary_to_list(cowboy_req:header(<<"x-test-pid">>, Req))),
  17. Test = binary_to_atom(cowboy_req:header(<<"x-test-case">>, Req), latin1),
  18. State = #state{pid=Pid, test=Test},
  19. Pid ! {Pid, self(), init, StreamID, Req, Opts},
  20. {init_commands(StreamID, Req, State), State}.
  21. init_commands(_, _, #state{test=crash_in_init}) ->
  22. error(crash);
  23. init_commands(_, _, #state{test=crash_in_data}) ->
  24. [];
  25. init_commands(_, _, #state{test=crash_in_info}) ->
  26. [];
  27. init_commands(_, _, #state{test=crash_in_terminate}) ->
  28. [{response, 200, #{<<"content-length">> => <<"12">>}, <<"Hello world!">>}, stop];
  29. init_commands(_, _, #state{test=crash_in_early_error}) ->
  30. error(crash);
  31. init_commands(_, _, #state{test=flow_after_body_fully_read}) ->
  32. [];
  33. init_commands(_, _, #state{test=set_options_ignore_unknown}) ->
  34. [
  35. {set_options, #{unknown_options => true}},
  36. {response, 200, #{<<"content-length">> => <<"12">>}, <<"Hello world!">>},
  37. stop
  38. ];
  39. init_commands(_, _, State=#state{test=shutdown_on_stream_stop}) ->
  40. Spawn = init_process(false, State),
  41. [{spawn, Spawn, 5000}, {headers, 200, #{}}, stop];
  42. init_commands(_, _, State=#state{test=shutdown_on_socket_close}) ->
  43. Spawn = init_process(false, State),
  44. [{spawn, Spawn, 5000}, {headers, 200, #{}}];
  45. init_commands(_, _, State=#state{test=shutdown_timeout_on_stream_stop}) ->
  46. Spawn = init_process(true, State),
  47. [{spawn, Spawn, 2000}, {headers, 200, #{}}, stop];
  48. init_commands(_, _, State=#state{test=shutdown_timeout_on_socket_close}) ->
  49. Spawn = init_process(true, State),
  50. [{spawn, Spawn, 2000}, {headers, 200, #{}}];
  51. init_commands(_, _, State=#state{test=switch_protocol_after_headers}) ->
  52. [{headers, 200, #{}}, {switch_protocol, #{}, ?MODULE, State}];
  53. init_commands(_, _, State=#state{test=switch_protocol_after_headers_data}) ->
  54. [{headers, 200, #{}}, {data, fin, <<"{}">>}, {switch_protocol, #{}, ?MODULE, State}];
  55. init_commands(_, _, State=#state{test=switch_protocol_after_response}) ->
  56. [{response, 200, #{}, <<"{}">>}, {switch_protocol, #{}, ?MODULE, State}];
  57. init_commands(_, _, State=#state{test=terminate_on_switch_protocol}) ->
  58. [{switch_protocol, #{}, ?MODULE, State}];
  59. init_commands(_, _, #state{test=terminate_on_stop}) ->
  60. [{response, 204, #{}, <<>>}];
  61. init_commands(_, _, _) ->
  62. [{headers, 200, #{}}].
  63. init_process(TrapExit, #state{pid=Pid}) ->
  64. Self = self(),
  65. Spawn = spawn_link(fun() ->
  66. process_flag(trap_exit, TrapExit),
  67. Pid ! {Pid, Self, spawned, self()},
  68. receive {Pid, ready} -> ok after 1000 -> error(timeout) end,
  69. Self ! {self(), ready},
  70. receive after 5000 ->
  71. Pid ! {Pid, Self, still_alive, self()}
  72. end
  73. end),
  74. receive {Spawn, ready} -> ok after 1000 -> error(timeout) end,
  75. Spawn.
  76. data(_, _, _, #state{test=crash_in_data}) ->
  77. error(crash);
  78. data(_, fin, <<"Hello world!">>, State=#state{test=flow_after_body_fully_read}) ->
  79. {[{flow, 12}, {response, 200, #{}, <<"{}">>}], State};
  80. data(StreamID, IsFin, Data, State=#state{pid=Pid}) ->
  81. Pid ! {Pid, self(), data, StreamID, IsFin, Data, State},
  82. {[], State}.
  83. info(_, Resp={response, _, _, _}, State) ->
  84. {[Resp], State};
  85. info(_, crash, #state{test=crash_in_info}) ->
  86. error(crash);
  87. info(StreamID, Info, State=#state{pid=Pid}) ->
  88. Pid ! {Pid, self(), info, StreamID, Info, State},
  89. case Info of
  90. please_stop -> {[stop], State};
  91. _ -> {[Info], State}
  92. end.
  93. terminate(StreamID, Reason, State=#state{pid=Pid, test=crash_in_terminate}) ->
  94. Pid ! {Pid, self(), terminate, StreamID, Reason, State},
  95. error(crash);
  96. terminate(StreamID, Reason, State=#state{pid=Pid}) ->
  97. Pid ! {Pid, self(), terminate, StreamID, Reason, State},
  98. ok.
  99. %% This clause can only test for early errors that reached the required headers.
  100. early_error(StreamID, Reason, PartialReq, Resp, Opts) ->
  101. Pid = list_to_pid(binary_to_list(cowboy_req:header(<<"x-test-pid">>, PartialReq))),
  102. Pid ! {Pid, self(), early_error, StreamID, Reason, PartialReq, Resp, Opts},
  103. case cowboy_req:header(<<"x-test-case">>, PartialReq) of
  104. <<"crash_in_early_error",_/bits>> -> error(crash);
  105. _ -> Resp
  106. end.
  107. %% @todo It would be good if we could allow this function to return normally.
  108. -spec takeover(_, _, _, _, _, _, _) -> no_return().
  109. takeover(Parent, Ref, Socket, Transport, Opts, Buffer, State=#state{pid=Pid}) ->
  110. Pid ! {Pid, self(), takeover, Parent, Ref, Socket, Transport, Opts, Buffer, State},
  111. exit(normal).