No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

283 líneas
11 KiB

hace 14 años
hace 14 años
hace 14 años
hace 14 años
hace 14 años
hace 14 años
hace 14 años
hace 14 años
hace 13 años
hace 14 años
hace 14 años
hace 14 años
hace 14 años
hace 14 años
hace 14 años
hace 14 años
hace 14 años
hace 14 años
hace 14 años
hace 14 años
hace 13 años
hace 13 años
hace 14 años
hace 14 años
  1. %%% File : ibrowse_test_server.erl
  2. %%% Author : Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
  3. %%% Benjamin Lee <http://github.com/benjaminplee>
  4. %%% Dan Schwabe <http://github.com/dfschwabe>
  5. %%% Brian Richards <http://github.com/richbria>
  6. %%% Description : A server to simulate various test scenarios
  7. %%% Created : 17 Oct 2010 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
  8. -module(ibrowse_test_server).
  9. -export([
  10. start_server/2,
  11. stop_server/1,
  12. get_conn_pipeline_depth/0
  13. ]).
  14. -record(request, {method, uri, version, headers = [], body = []}).
  15. -define(dec2hex(X), erlang:integer_to_list(X, 16)).
  16. -define(ACCEPT_TIMEOUT_MS, 1000).
  17. -define(CONN_PIPELINE_DEPTH, conn_pipeline_depth).
  18. start_server(Port, Sock_type) ->
  19. Fun = fun() ->
  20. Name = server_proc_name(Port),
  21. register(Name, self()),
  22. ets:new(?CONN_PIPELINE_DEPTH, [named_table, public, set]),
  23. case do_listen(Sock_type, Port, [{active, false},
  24. {reuseaddr, true},
  25. {nodelay, true},
  26. {packet, http}]) of
  27. {ok, Sock} ->
  28. do_trace("Server listening on port: ~p~n", [Port]),
  29. accept_loop(Sock, Sock_type);
  30. Err ->
  31. erlang:error(
  32. lists:flatten(
  33. io_lib:format(
  34. "Failed to start server on port ~p. ~p~n",
  35. [Port, Err]))),
  36. exit({listen_error, Err})
  37. end,
  38. unregister(Name)
  39. end,
  40. spawn_link(Fun).
  41. stop_server(Port) ->
  42. server_proc_name(Port) ! stop,
  43. timer:sleep(2000), % wait for server to receive msg and unregister
  44. ok.
  45. get_conn_pipeline_depth() ->
  46. ets:tab2list(?CONN_PIPELINE_DEPTH).
  47. server_proc_name(Port) ->
  48. list_to_atom("ibrowse_test_server_"++integer_to_list(Port)).
  49. do_listen(tcp, Port, Opts) ->
  50. gen_tcp:listen(Port, Opts);
  51. do_listen(ssl, Port, Opts) ->
  52. application:start(crypto),
  53. application:start(ssl),
  54. ssl:listen(Port, Opts).
  55. do_accept(tcp, Listen_sock) ->
  56. gen_tcp:accept(Listen_sock, ?ACCEPT_TIMEOUT_MS);
  57. do_accept(ssl, Listen_sock) ->
  58. ssl:ssl_accept(Listen_sock, ?ACCEPT_TIMEOUT_MS).
  59. accept_loop(Sock, Sock_type) ->
  60. case do_accept(Sock_type, Sock) of
  61. {ok, Conn} ->
  62. Pid = spawn_link(fun() -> connection(Conn, Sock_type) end),
  63. set_controlling_process(Conn, Sock_type, Pid),
  64. Pid ! {setopts, [{active, true}]},
  65. accept_loop(Sock, Sock_type);
  66. {error, timeout} ->
  67. receive
  68. stop ->
  69. ok
  70. after 10 ->
  71. accept_loop(Sock, Sock_type)
  72. end;
  73. Err ->
  74. Err
  75. end.
  76. connection(Conn, Sock_type) ->
  77. ets:insert(?CONN_PIPELINE_DEPTH, {self(), 0}),
  78. try
  79. server_loop(Conn, Sock_type, #request{})
  80. after
  81. ets:delete(?CONN_PIPELINE_DEPTH, self())
  82. end.
  83. set_controlling_process(Sock, tcp, Pid) ->
  84. gen_tcp:controlling_process(Sock, Pid);
  85. set_controlling_process(Sock, ssl, Pid) ->
  86. ssl:controlling_process(Sock, Pid).
  87. setopts(Sock, tcp, Opts) ->
  88. inet:setopts(Sock, Opts);
  89. setopts(Sock, ssl, Opts) ->
  90. ssl:setopts(Sock, Opts).
  91. server_loop(Sock, Sock_type, #request{headers = Headers} = Req) ->
  92. receive
  93. {http, Sock, {http_request, HttpMethod, HttpUri, HttpVersion}} ->
  94. ets:update_counter(?CONN_PIPELINE_DEPTH, self(), 1),
  95. server_loop(Sock, Sock_type, Req#request{method = HttpMethod,
  96. uri = HttpUri,
  97. version = HttpVersion});
  98. {http, Sock, {http_header, _, _, _, _} = H} ->
  99. server_loop(Sock, Sock_type, Req#request{headers = [H | Headers]});
  100. {http, Sock, http_eoh} ->
  101. case process_request(Sock, Sock_type, Req) of
  102. not_done ->
  103. ok;
  104. _ ->
  105. ets:update_counter(?CONN_PIPELINE_DEPTH, self(), -1)
  106. end,
  107. server_loop(Sock, Sock_type, #request{});
  108. {http, Sock, {http_error, Err}} ->
  109. do_trace("Error parsing HTTP request:~n"
  110. "Req so far : ~p~n"
  111. "Err : ", [Req, Err]),
  112. exit({http_error, Err});
  113. {setopts, Opts} ->
  114. setopts(Sock, Sock_type, Opts),
  115. server_loop(Sock, Sock_type, Req);
  116. {tcp_closed, Sock} ->
  117. do_trace("Client closed connection~n", []),
  118. ok;
  119. Other ->
  120. do_trace("Recvd unknown msg: ~p~n", [Other]),
  121. exit({unknown_msg, Other})
  122. after 5000 ->
  123. do_trace("Timing out client connection~n", []),
  124. ok
  125. end.
  126. do_trace(Fmt, Args) ->
  127. do_trace(get(my_trace_flag), Fmt, Args).
  128. do_trace(true, Fmt, Args) ->
  129. io:format("~s -- " ++ Fmt, [ibrowse_lib:printable_date() | Args]);
  130. do_trace(_, _, _) ->
  131. ok.
  132. process_request(Sock, Sock_type,
  133. #request{method='GET',
  134. headers = Headers,
  135. uri = {abs_path, "/ibrowse_stream_once_chunk_pipeline_test"}} = Req) ->
  136. Req_id = case lists:keysearch("X-Ibrowse-Request-Id", 3, Headers) of
  137. false ->
  138. "";
  139. {value, {http_header, _, _, _, Req_id_1}} ->
  140. Req_id_1
  141. end,
  142. Req_id_header = ["x-ibrowse-request-id: ", Req_id, "\r\n"],
  143. do_trace("Recvd req: ~p~n", [Req]),
  144. Body = string:join([integer_to_list(X) || X <- lists:seq(1,100)], "-"),
  145. Chunked_body = chunk_request_body(Body, 50),
  146. Resp_1 = [<<"HTTP/1.1 200 OK\r\n">>,
  147. Req_id_header,
  148. <<"Transfer-Encoding: chunked\r\n\r\n">>],
  149. Resp_2 = Chunked_body,
  150. do_send(Sock, Sock_type, Resp_1),
  151. timer:sleep(100),
  152. do_send(Sock, Sock_type, Resp_2);
  153. process_request(Sock, Sock_type,
  154. #request{method='GET',
  155. headers = _Headers,
  156. uri = {abs_path, "/ibrowse_inac_timeout_test"}} = Req) ->
  157. do_trace("Recvd req: ~p. Sleeping for 30 secs...~n", [Req]),
  158. timer:sleep(30000),
  159. do_trace("...Sending response now.~n", []),
  160. Resp = <<"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n">>,
  161. do_send(Sock, Sock_type, Resp);
  162. process_request(Sock, Sock_type,
  163. #request{method='HEAD',
  164. headers = _Headers,
  165. uri = {abs_path, "/ibrowse_head_transfer_enc"}}) ->
  166. Resp = <<"HTTP/1.1 400 Bad Request\r\nServer: Apache-Coyote/1.1\r\nContent-Length:5\r\nDate: Wed, 04 Apr 2012 16:53:49 GMT\r\n\r\nabcde">>,
  167. do_send(Sock, Sock_type, Resp);
  168. process_request(Sock, Sock_type,
  169. #request{method='GET',
  170. headers = Headers,
  171. uri = {abs_path, "/ibrowse_echo_header"}}) ->
  172. Tag = "x-binary",
  173. Headers_1 = [{to_lower(X), to_lower(Y)} || {http_header, _, X, _, Y} <- Headers],
  174. X_binary_header_val = case lists:keysearch(Tag, 1, Headers_1) of
  175. false ->
  176. "not_found";
  177. {value, {_, V}} ->
  178. V
  179. end,
  180. Resp = [<<"HTTP/1.1 200 OK\r\n">>,
  181. <<"Server: ibrowse_test\r\n">>,
  182. Tag, ": ", X_binary_header_val, "\r\n",
  183. <<"Content-Length: 0\r\n\r\n">>],
  184. do_send(Sock, Sock_type, Resp);
  185. process_request(Sock, Sock_type,
  186. #request{method='HEAD',
  187. headers = _Headers,
  188. uri = {abs_path, "/ibrowse_head_test"}}) ->
  189. Resp = <<"HTTP/1.1 200 OK\r\nServer: Apache-Coyote/1.1\r\nTransfer-Encoding: chunked\r\nDate: Wed, 04 Apr 2012 16:53:49 GMT\r\nConnection: close\r\n\r\n">>,
  190. do_send(Sock, Sock_type, Resp);
  191. process_request(Sock, Sock_type,
  192. #request{method='POST',
  193. headers = _Headers,
  194. uri = {abs_path, "/ibrowse_303_no_body_test"}}) ->
  195. Resp = <<"HTTP/1.1 303 See Other\r\nLocation: http://example.org\r\n">>,
  196. do_send(Sock, Sock_type, Resp);
  197. process_request(Sock, Sock_type,
  198. #request{method='POST',
  199. headers = _Headers,
  200. uri = {abs_path, "/ibrowse_303_with_body_test"}}) ->
  201. Resp = <<"HTTP/1.1 303 See Other\r\nLocation: http://example.org\r\nContent-Length: 5\r\n\r\nabcde">>,
  202. do_send(Sock, Sock_type, Resp);
  203. process_request(_Sock, _Sock_type, #request{uri = {abs_path, "/never_respond"} } ) ->
  204. not_done;
  205. process_request(Sock, Sock_type, Req) ->
  206. do_trace("Recvd req: ~p~n", [Req]),
  207. Resp = <<"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n">>,
  208. do_send(Sock, Sock_type, Resp).
  209. do_send(Sock, tcp, Resp) ->
  210. gen_tcp:send(Sock, Resp);
  211. do_send(Sock, ssl, Resp) ->
  212. ssl:send(Sock, Resp).
  213. %%------------------------------------------------------------------------------
  214. %% Utility functions
  215. %%------------------------------------------------------------------------------
  216. chunk_request_body(Body, _ChunkSize) when is_tuple(Body) orelse
  217. is_function(Body) ->
  218. Body;
  219. chunk_request_body(Body, ChunkSize) ->
  220. chunk_request_body(Body, ChunkSize, []).
  221. chunk_request_body(Body, _ChunkSize, Acc) when Body == <<>>; Body == [] ->
  222. LastChunk = "0\r\n",
  223. lists:reverse(["\r\n", LastChunk | Acc]);
  224. chunk_request_body(Body, ChunkSize, Acc) when is_binary(Body),
  225. size(Body) >= ChunkSize ->
  226. <<ChunkBody:ChunkSize/binary, Rest/binary>> = Body,
  227. Chunk = [?dec2hex(ChunkSize),"\r\n",
  228. ChunkBody, "\r\n"],
  229. chunk_request_body(Rest, ChunkSize, [Chunk | Acc]);
  230. chunk_request_body(Body, _ChunkSize, Acc) when is_binary(Body) ->
  231. BodySize = size(Body),
  232. Chunk = [?dec2hex(BodySize),"\r\n",
  233. Body, "\r\n"],
  234. LastChunk = "0\r\n",
  235. lists:reverse(["\r\n", LastChunk, Chunk | Acc]);
  236. chunk_request_body(Body, ChunkSize, Acc) when length(Body) >= ChunkSize ->
  237. {ChunkBody, Rest} = split_list_at(Body, ChunkSize),
  238. Chunk = [?dec2hex(ChunkSize),"\r\n",
  239. ChunkBody, "\r\n"],
  240. chunk_request_body(Rest, ChunkSize, [Chunk | Acc]);
  241. chunk_request_body(Body, _ChunkSize, Acc) when is_list(Body) ->
  242. BodySize = length(Body),
  243. Chunk = [?dec2hex(BodySize),"\r\n",
  244. Body, "\r\n"],
  245. LastChunk = "0\r\n",
  246. lists:reverse(["\r\n", LastChunk, Chunk | Acc]).
  247. split_list_at(List, N) ->
  248. split_list_at(List, N, []).
  249. split_list_at([], _, Acc) ->
  250. {lists:reverse(Acc), []};
  251. split_list_at(List2, 0, List1) ->
  252. {lists:reverse(List1), List2};
  253. split_list_at([H | List2], N, List1) ->
  254. split_list_at(List2, N-1, [H | List1]).
  255. to_lower(X) when is_atom(X) ->
  256. list_to_atom(to_lower(atom_to_list(X)));
  257. to_lower(X) when is_list(X) ->
  258. string:to_lower(X).