您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

191 行
6.9 KiB

  1. %%% File : ibrowse_test_server.erl
  2. %%% Author : Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
  3. %%% Description : A server to simulate various test scenarios
  4. %%% Created : 17 Oct 2010 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
  5. -module(ibrowse_test_server).
  6. -export([
  7. start_server/2,
  8. stop_server/1
  9. ]).
  10. -record(request, {method, uri, version, headers = [], body = []}).
  11. -define(dec2hex(X), erlang:integer_to_list(X, 16)).
  12. start_server(Port, Sock_type) ->
  13. Fun = fun() ->
  14. register(server_proc_name(Port), self()),
  15. case do_listen(Sock_type, Port, [{active, false},
  16. {nodelay, true},
  17. {packet, http}]) of
  18. {ok, Sock} ->
  19. do_trace("Server listening on port: ~p~n", [Port]),
  20. accept_loop(Sock, Sock_type);
  21. Err ->
  22. do_trace("Failed to start server on port ~p. ~p~n",
  23. [Port, Err]),
  24. Err
  25. end
  26. end,
  27. spawn(Fun).
  28. stop_server(Port) ->
  29. exit(whereis(server_proc_name(Port)), kill).
  30. server_proc_name(Port) ->
  31. list_to_atom("ibrowse_test_server_"++integer_to_list(Port)).
  32. do_listen(tcp, Port, Opts) ->
  33. gen_tcp:listen(Port, Opts);
  34. do_listen(ssl, Port, Opts) ->
  35. application:start(crypto),
  36. application:start(ssl),
  37. ssl:listen(Port, Opts).
  38. do_accept(tcp, Listen_sock) ->
  39. gen_tcp:accept(Listen_sock);
  40. do_accept(ssl, Listen_sock) ->
  41. ssl:ssl_accept(Listen_sock).
  42. accept_loop(Sock, Sock_type) ->
  43. case do_accept(Sock_type, Sock) of
  44. {ok, Conn} ->
  45. Pid = spawn_link(
  46. fun() ->
  47. server_loop(Conn, Sock_type, #request{})
  48. end),
  49. set_controlling_process(Conn, Sock_type, Pid),
  50. Pid ! {setopts, [{active, true}]},
  51. accept_loop(Sock, Sock_type);
  52. Err ->
  53. Err
  54. end.
  55. set_controlling_process(Sock, tcp, Pid) ->
  56. gen_tcp:controlling_process(Sock, Pid);
  57. set_controlling_process(Sock, ssl, Pid) ->
  58. ssl:controlling_process(Sock, Pid).
  59. setopts(Sock, tcp, Opts) ->
  60. inet:setopts(Sock, Opts);
  61. setopts(Sock, ssl, Opts) ->
  62. ssl:setopts(Sock, Opts).
  63. server_loop(Sock, Sock_type, #request{headers = Headers} = Req) ->
  64. receive
  65. {http, Sock, {http_request, HttpMethod, HttpUri, HttpVersion}} ->
  66. server_loop(Sock, Sock_type, Req#request{method = HttpMethod,
  67. uri = HttpUri,
  68. version = HttpVersion});
  69. {http, Sock, {http_header, _, _, _, _} = H} ->
  70. server_loop(Sock, Sock_type, Req#request{headers = [H | Headers]});
  71. {http, Sock, http_eoh} ->
  72. process_request(Sock, Sock_type, Req),
  73. server_loop(Sock, Sock_type, #request{});
  74. {http, Sock, {http_error, Err}} ->
  75. do_trace("Error parsing HTTP request:~n"
  76. "Req so far : ~p~n"
  77. "Err : ", [Req, Err]),
  78. exit({http_error, Err});
  79. {setopts, Opts} ->
  80. setopts(Sock, Sock_type, Opts),
  81. server_loop(Sock, Sock_type, Req);
  82. {tcp_closed, Sock} ->
  83. do_trace("Client closed connection~n", []),
  84. ok;
  85. Other ->
  86. do_trace("Recvd unknown msg: ~p~n", [Other]),
  87. exit({unknown_msg, Other})
  88. after 5000 ->
  89. do_trace("Timing out client connection~n", []),
  90. ok
  91. end.
  92. do_trace(Fmt, Args) ->
  93. do_trace(get(my_trace_flag), Fmt, Args).
  94. do_trace(true, Fmt, Args) ->
  95. io:format("~s -- " ++ Fmt, [ibrowse_lib:printable_date() | Args]);
  96. do_trace(_, _, _) ->
  97. ok.
  98. process_request(Sock, Sock_type,
  99. #request{method='GET',
  100. headers = Headers,
  101. uri = {abs_path, "/ibrowse_stream_once_chunk_pipeline_test"}} = Req) ->
  102. Req_id = case lists:keysearch("X-Ibrowse-Request-Id", 3, Headers) of
  103. false ->
  104. "";
  105. {value, {http_header, _, _, _, Req_id_1}} ->
  106. Req_id_1
  107. end,
  108. Req_id_header = ["x-ibrowse-request-id: ", Req_id, "\r\n"],
  109. do_trace("Recvd req: ~p~n", [Req]),
  110. Body = string:join([integer_to_list(X) || X <- lists:seq(1,100)], "-"),
  111. Chunked_body = chunk_request_body(Body, 50),
  112. Resp_1 = [<<"HTTP/1.1 200 OK\r\n">>,
  113. Req_id_header,
  114. <<"Transfer-Encoding: chunked\r\n\r\n">>],
  115. Resp_2 = Chunked_body,
  116. do_send(Sock, Sock_type, Resp_1),
  117. timer:sleep(100),
  118. do_send(Sock, Sock_type, Resp_2);
  119. process_request(Sock, Sock_type, Req) ->
  120. do_trace("Recvd req: ~p~n", [Req]),
  121. Resp = <<"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n">>,
  122. do_send(Sock, Sock_type, Resp).
  123. do_send(Sock, tcp, Resp) ->
  124. ok = gen_tcp:send(Sock, Resp);
  125. do_send(Sock, ssl, Resp) ->
  126. ok = ssl:send(Sock, Resp).
  127. %%------------------------------------------------------------------------------
  128. %% Utility functions
  129. %%------------------------------------------------------------------------------
  130. chunk_request_body(Body, _ChunkSize) when is_tuple(Body) orelse
  131. is_function(Body) ->
  132. Body;
  133. chunk_request_body(Body, ChunkSize) ->
  134. chunk_request_body(Body, ChunkSize, []).
  135. chunk_request_body(Body, _ChunkSize, Acc) when Body == <<>>; Body == [] ->
  136. LastChunk = "0\r\n",
  137. lists:reverse(["\r\n", LastChunk | Acc]);
  138. chunk_request_body(Body, ChunkSize, Acc) when is_binary(Body),
  139. size(Body) >= ChunkSize ->
  140. <<ChunkBody:ChunkSize/binary, Rest/binary>> = Body,
  141. Chunk = [?dec2hex(ChunkSize),"\r\n",
  142. ChunkBody, "\r\n"],
  143. chunk_request_body(Rest, ChunkSize, [Chunk | Acc]);
  144. chunk_request_body(Body, _ChunkSize, Acc) when is_binary(Body) ->
  145. BodySize = size(Body),
  146. Chunk = [?dec2hex(BodySize),"\r\n",
  147. Body, "\r\n"],
  148. LastChunk = "0\r\n",
  149. lists:reverse(["\r\n", LastChunk, Chunk | Acc]);
  150. chunk_request_body(Body, ChunkSize, Acc) when length(Body) >= ChunkSize ->
  151. {ChunkBody, Rest} = split_list_at(Body, ChunkSize),
  152. Chunk = [?dec2hex(ChunkSize),"\r\n",
  153. ChunkBody, "\r\n"],
  154. chunk_request_body(Rest, ChunkSize, [Chunk | Acc]);
  155. chunk_request_body(Body, _ChunkSize, Acc) when is_list(Body) ->
  156. BodySize = length(Body),
  157. Chunk = [?dec2hex(BodySize),"\r\n",
  158. Body, "\r\n"],
  159. LastChunk = "0\r\n",
  160. lists:reverse(["\r\n", LastChunk, Chunk | Acc]).
  161. split_list_at(List, N) ->
  162. split_list_at(List, N, []).
  163. split_list_at([], _, Acc) ->
  164. {lists:reverse(Acc), []};
  165. split_list_at(List2, 0, List1) ->
  166. {lists:reverse(List1), List2};
  167. split_list_at([H | List2], N, List1) ->
  168. split_list_at(List2, N-1, [H | List1]).