PageRenderTime 278ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/ewgi_api.erl

http://github.com/skarab/ewgi
Erlang | 918 lines | 611 code | 142 blank | 165 comment | 4 complexity | 2b384630705aef0436e47e78fd7cb050 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. %%%-------------------------------------------------------------------
  2. %%% File : ewgi_api.erl
  3. %%% Authors : Filippo Pacini <filippo.pacini@gmail.com>
  4. %%% Hunter Morris <huntermorris@gmail.com>
  5. %%% License :
  6. %%% The contents of this file are subject to the Mozilla Public
  7. %%% License Version 1.1 (the "License"); you may not use this file
  8. %%% except in compliance with the License. You may obtain a copy of
  9. %%% the License at http://www.mozilla.org/MPL/
  10. %%%
  11. %%% Software distributed under the License is distributed on an "AS IS"
  12. %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  13. %%% the License for the specific language governing rights and
  14. %%% limitations under the License.
  15. %%% The Initial Developer of the Original Code is S.G. Consulting
  16. %%% srl. Portions created by S.G. Consulting s.r.l. are Copyright (C)
  17. %%% 2007 S.G. Consulting srl. All Rights Reserved.
  18. %%%
  19. %%% @doc
  20. %%% <p>ewgi API. Defines a low level CGI like API.</p>
  21. %%%
  22. %%% @end
  23. %%%
  24. %%% Created : 10 Oct 2007 by Filippo Pacini <filippo.pacini@gmail.com>
  25. %%%-------------------------------------------------------------------
  26. -module(ewgi_api).
  27. -include_lib("ewgi.hrl").
  28. %% Record helpers
  29. -export([empty_request/0, empty_response/0, context/2, request/2, response/2,
  30. request/1, response/1]).
  31. -export([response_headers/1, response_message_body/1, response_status/1,
  32. response_error/1]).
  33. -export([response_headers/2, response_message_body/2, response_status/2,
  34. response_error/2]).
  35. %% Request 'get' methods
  36. -export([auth_type/1, content_length/1, content_type/1, gateway_interface/1,
  37. path_info/1, path_translated/1, query_string/1, remote_addr/1,
  38. remote_host/1, remote_ident/1, remote_user/1, remote_user_data/1,
  39. request_method/1, script_name/1, server_name/1, server_port/1,
  40. server_protocol/1, server_software/1]).
  41. %% Request 'set' methods
  42. -export([auth_type/2, content_length/2, content_type/2, gateway_interface/2,
  43. path_info/2, path_translated/2, query_string/2, remote_addr/2,
  44. remote_host/2, remote_ident/2, remote_user/2, remote_user_data/2,
  45. request_method/2, script_name/2, server_name/2, server_port/2,
  46. server_protocol/2, server_software/2]).
  47. %% Additional request methods
  48. -export([get_header_value/2, set_header/3, insert_header/3,
  49. get_all_headers/1, read_input/1, read_input/3, read_input_string/2,
  50. write_error/2, url_scheme/1, version/1, get_all_data/1, find_data/2,
  51. find_data/3, store_data/3]).
  52. %% Server methods
  53. -export([server_request_foldl/4]).
  54. %% Utility methods
  55. -export([parse_qs/1, parse_post/1, urlencode/1, quote/1, normalize_header/1,
  56. unquote_path/1, path_components/3, urlsplit/1]).
  57. %% Stream methods
  58. -export([
  59. stream_process_init/2,
  60. stream_process_init/3,
  61. stream_process_deliver/2,
  62. stream_process_deliver_chunk/2,
  63. stream_process_deliver_final_chunk/2,
  64. stream_process_end/1
  65. ]).
  66. %%====================================================================
  67. %% API
  68. %%====================================================================
  69. -spec empty_request() -> ewgi_request().
  70. empty_request() ->
  71. {'ewgi_request', undefined, undefined, undefined,
  72. empty_ewgi_spec(), undefined, empty_http_headers(), undefined,
  73. undefined, undefined, undefined, undefined, undefined, undefined,
  74. undefined, undefined, undefined, undefined, undefined, undefined,
  75. undefined}.
  76. -spec empty_response() -> ewgi_response().
  77. empty_response() ->
  78. {'ewgi_response', undefined, [], [], undefined}.
  79. -spec context(ewgi_request(), ewgi_response()) -> ewgi_context().
  80. context(Request, Response) when ?IS_EWGI_REQUEST(Request),
  81. ?IS_EWGI_RESPONSE(Response) ->
  82. {'ewgi_context', Request, Response}.
  83. -spec request(ewgi_request(), ewgi_context()) -> ewgi_context().
  84. request(Req, Ctx) when ?IS_EWGI_REQUEST(Req), ?IS_EWGI_CONTEXT(Ctx) ->
  85. ?SET_EWGI_REQUEST(Req, Ctx).
  86. -spec response(ewgi_response(), ewgi_context()) -> ewgi_context().
  87. response(Rsp, Ctx) when ?IS_EWGI_RESPONSE(Rsp),
  88. ?IS_EWGI_CONTEXT(Ctx) ->
  89. ?SET_EWGI_RESPONSE(Rsp, Ctx).
  90. -spec response(ewgi_context()) -> ewgi_response().
  91. response(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  92. ?GET_EWGI_RESPONSE(Ctx).
  93. -spec request(ewgi_context()) -> ewgi_request().
  94. request(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  95. ?GET_EWGI_REQUEST(Ctx).
  96. -spec response_headers(ewgi_context()) -> ewgi_header_list().
  97. response_headers(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  98. ?GET_RESPONSE_HEADERS(response(Ctx)).
  99. -spec response_status(ewgi_context()) -> ewgi_status().
  100. response_status(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  101. ?GET_RESPONSE_STATUS(response(Ctx)).
  102. -spec response_message_body(ewgi_context()) -> ewgi_message_body().
  103. response_message_body(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  104. ?GET_RESPONSE_MESSAGE_BODY(response(Ctx)).
  105. -spec response_error(ewgi_context()) -> any().
  106. response_error(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  107. ?GET_RESPONSE_ERROR(response(Ctx)).
  108. -spec response_headers(ewgi_header_list(), ewgi_context()) -> ewgi_context().
  109. response_headers(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  110. response(?SET_RESPONSE_HEADERS(V, response(Ctx)), Ctx).
  111. -spec response_status(ewgi_status(), ewgi_context()) -> ewgi_context().
  112. response_status(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  113. response(?SET_RESPONSE_STATUS(V, response(Ctx)), Ctx).
  114. -spec response_message_body(ewgi_message_body(), ewgi_context()) -> ewgi_context().
  115. response_message_body(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  116. response(?SET_RESPONSE_MESSAGE_BODY(V, response(Ctx)), Ctx).
  117. -spec response_error(any(), ewgi_context()) -> ewgi_context().
  118. response_error(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  119. response(?SET_RESPONSE_ERROR(V, response(Ctx)), Ctx).
  120. -spec auth_type(ewgi_context()) -> ewgi_val().
  121. auth_type(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  122. ?GET_AUTH_TYPE(request(Ctx)).
  123. -spec content_length(ewgi_context()) -> non_neg_integer().
  124. content_length(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  125. ?GET_CONTENT_LENGTH(request(Ctx)).
  126. -spec content_type(ewgi_context()) -> ewgi_val().
  127. content_type(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  128. ?GET_CONTENT_TYPE(request(Ctx)).
  129. -spec gateway_interface(ewgi_context()) -> ewgi_val().
  130. gateway_interface(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  131. ?GET_GATEWAY_INTERFACE(request(Ctx)).
  132. -spec path_info(ewgi_context()) -> ewgi_val().
  133. path_info(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  134. ?GET_PATH_INFO(request(Ctx)).
  135. -spec path_translated(ewgi_context()) -> ewgi_val().
  136. path_translated(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  137. ?GET_PATH_TRANSLATED(request(Ctx)).
  138. -spec query_string(ewgi_context()) -> ewgi_val().
  139. query_string(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  140. ?GET_QUERY_STRING(request(Ctx)).
  141. -spec remote_addr(ewgi_context()) -> ewgi_val().
  142. remote_addr(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  143. ?GET_REMOTE_ADDR(request(Ctx)).
  144. -spec remote_host(ewgi_context()) -> ewgi_val().
  145. remote_host(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  146. ?GET_REMOTE_HOST(request(Ctx)).
  147. -spec remote_ident(ewgi_context()) -> ewgi_val().
  148. remote_ident(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  149. ?GET_REMOTE_IDENT(request(Ctx)).
  150. -spec remote_user(ewgi_context()) -> ewgi_val().
  151. remote_user(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  152. ?GET_REMOTE_USER(request(Ctx)).
  153. -spec remote_user_data(ewgi_context()) -> ewgi_val().
  154. remote_user_data(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  155. ?GET_REMOTE_USER_DATA(request(Ctx)).
  156. -spec request_method(ewgi_context()) -> ewgi_request_method().
  157. request_method(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  158. ?GET_REQUEST_METHOD(request(Ctx)).
  159. -spec script_name(ewgi_context()) -> ewgi_val().
  160. script_name(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  161. ?GET_SCRIPT_NAME(request(Ctx)).
  162. -spec server_name(ewgi_context()) -> ewgi_val().
  163. server_name(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  164. ?GET_SERVER_NAME(request(Ctx)).
  165. -spec server_port(ewgi_context()) -> ewgi_val().
  166. server_port(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  167. ?GET_SERVER_PORT(request(Ctx)).
  168. -spec server_protocol(ewgi_context()) -> ewgi_val().
  169. server_protocol(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  170. ?GET_SERVER_PROTOCOL(request(Ctx)).
  171. -spec server_software(ewgi_context()) -> ewgi_val().
  172. server_software(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  173. ?GET_SERVER_SOFTWARE(request(Ctx)).
  174. -spec headers(ewgi_context()) -> ewgi_http_headers().
  175. headers(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  176. ?GET_HTTP_HEADERS(request(Ctx)).
  177. -spec headers(ewgi_http_headers(), ewgi_context()) -> ewgi_context().
  178. headers(H, Ctx) when ?IS_HTTP_HEADERS(H), ?IS_EWGI_CONTEXT(Ctx) ->
  179. request(?SET_HTTP_HEADERS(H, request(Ctx)), Ctx).
  180. -spec get_header_value(string(), ewgi_context()) -> ewgi_header_val().
  181. get_header_value(Hdr0, Ctx) when is_list(Hdr0), ?IS_EWGI_CONTEXT(Ctx) ->
  182. Hdr = string:to_lower(Hdr0),
  183. get_header1(Hdr, Ctx).
  184. get_header1("accept", Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  185. ?GET_HTTP_ACCEPT(headers(Ctx));
  186. get_header1("cookie", Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  187. ?GET_HTTP_COOKIE(headers(Ctx));
  188. get_header1("host", Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  189. ?GET_HTTP_HOST(headers(Ctx));
  190. get_header1("if-modified-since", Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  191. ?GET_HTTP_IF_MODIFIED_SINCE(headers(Ctx));
  192. get_header1("user-agent", Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  193. ?GET_HTTP_USER_AGENT(headers(Ctx));
  194. get_header1("x-http-method-override", Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  195. ?GET_HTTP_X_HTTP_METHOD_OVERRIDE(headers(Ctx));
  196. get_header1(Hdr, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  197. case gb_trees:lookup(Hdr, ?GET_HTTP_OTHER(headers(Ctx))) of
  198. {value, V} ->
  199. unzip_header_value(V);
  200. none ->
  201. undefined
  202. end.
  203. unzip_header_value([{_,_}|_]=V) ->
  204. {_, V1} = lists:unzip(V),
  205. string:join(V1, ", ");
  206. unzip_header_value(V) ->
  207. V.
  208. insert_header(K0, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  209. K = string:to_lower(K0),
  210. insert_header1(K, K0, V, Ctx).
  211. combine_headers(K, V, Ctx) ->
  212. case get_header1(K, Ctx) of
  213. undefined ->
  214. V;
  215. S when is_list(S) ->
  216. string:join([S, V], ", ")
  217. end.
  218. insert_header1("accept"=K, _, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  219. V1 = combine_headers(K, V, Ctx),
  220. headers(?SET_HTTP_ACCEPT(V1, headers(Ctx)), Ctx);
  221. insert_header1("cookie"=K, _, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  222. V1 = combine_headers(K, V, Ctx),
  223. headers(?SET_HTTP_COOKIE(V1, headers(Ctx)), Ctx);
  224. insert_header1("host"=K, _, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  225. V1 = combine_headers(K, V, Ctx),
  226. headers(?SET_HTTP_HOST(V1, headers(Ctx)), Ctx);
  227. insert_header1("if-modified-since"=K, _, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  228. V1 = combine_headers(K, V, Ctx),
  229. headers(?SET_HTTP_IF_MODIFIED_SINCE(V1, headers(Ctx)), Ctx);
  230. insert_header1("user-agent"=K, _, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  231. V1 = combine_headers(K, V, Ctx),
  232. headers(?SET_HTTP_USER_AGENT(V1, headers(Ctx)), Ctx);
  233. insert_header1("x-http-method-override"=K, _, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  234. V1 = combine_headers(K, V, Ctx),
  235. headers(?SET_HTTP_X_HTTP_METHOD_OVERRIDE(V1, headers(Ctx)), Ctx);
  236. insert_header1(K, K0, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  237. D = t_insert_header(K, {K0, V}, ?GET_HTTP_OTHER(headers(Ctx))),
  238. headers(?SET_HTTP_OTHER(D, headers(Ctx)), Ctx).
  239. set_header(K0, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  240. K = string:to_lower(K0),
  241. set_header1(K, K0, V, Ctx).
  242. set_header1("accept", _, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  243. headers(?SET_HTTP_ACCEPT(V, headers(Ctx)), Ctx);
  244. set_header1("cookie", _, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  245. headers(?SET_HTTP_COOKIE(V, headers(Ctx)), Ctx);
  246. set_header1("host", _, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  247. headers(?SET_HTTP_HOST(V, headers(Ctx)), Ctx);
  248. set_header1("if-modified-since", _, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  249. headers(?SET_HTTP_IF_MODIFIED_SINCE(V, headers(Ctx)), Ctx);
  250. set_header1("user-agent", _, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  251. headers(?SET_HTTP_USER_AGENT(V, headers(Ctx)), Ctx);
  252. set_header1("x-http-method-override", _, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  253. headers(?SET_HTTP_X_HTTP_METHOD_OVERRIDE(V, headers(Ctx)), Ctx);
  254. set_header1(K, K0, V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  255. D = t_enter_header(K, {K0, V}, ?GET_HTTP_OTHER(headers(Ctx))),
  256. headers(?SET_HTTP_OTHER(D, headers(Ctx)), Ctx).
  257. auth_type(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  258. request(?SET_AUTH_TYPE(V, request(Ctx)), Ctx).
  259. content_length(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  260. request(?SET_CONTENT_LENGTH(V, request(Ctx)), Ctx).
  261. content_type(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  262. request(?SET_CONTENT_TYPE(V, request(Ctx)), Ctx).
  263. gateway_interface(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  264. request(?SET_GATEWAY_INTERFACE(V, request(Ctx)), Ctx).
  265. path_info(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  266. request(?SET_PATH_INFO(V, request(Ctx)), Ctx).
  267. path_translated(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  268. request(?SET_PATH_TRANSLATED(V, request(Ctx)), Ctx).
  269. query_string(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  270. request(?SET_QUERY_STRING(V, request(Ctx)), Ctx).
  271. remote_addr(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  272. request(?SET_REMOTE_ADDR(V, request(Ctx)), Ctx).
  273. remote_host(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  274. request(?SET_REMOTE_HOST(V, request(Ctx)), Ctx).
  275. remote_ident(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  276. request(?SET_REMOTE_IDENT(V, request(Ctx)), Ctx).
  277. remote_user(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  278. request(?SET_REMOTE_USER(V, request(Ctx)), Ctx).
  279. remote_user_data(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  280. request(?SET_REMOTE_USER_DATA(V, request(Ctx)), Ctx).
  281. request_method(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  282. request(?SET_REQUEST_METHOD(V, request(Ctx)), Ctx).
  283. script_name(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  284. request(?SET_SCRIPT_NAME(V, request(Ctx)), Ctx).
  285. server_name(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  286. request(?SET_SERVER_NAME(V, request(Ctx)), Ctx).
  287. server_port(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  288. request(?SET_SERVER_PORT(V, request(Ctx)), Ctx).
  289. server_protocol(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  290. request(?SET_SERVER_PROTOCOL(V, request(Ctx)), Ctx).
  291. server_software(V, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  292. request(?SET_SERVER_SOFTWARE(V, request(Ctx)), Ctx).
  293. get_all_headers(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  294. H = headers(Ctx),
  295. Other = gb_trees:to_list(?GET_HTTP_OTHER(H)),
  296. Acc = [{K, unzip_header_value(V)} || {K, V} <- Other],
  297. L = [{"accept", get_header_value("accept", Ctx)},
  298. {"cookie", get_header_value("cookie", Ctx)},
  299. {"host", get_header_value("host", Ctx)},
  300. {"if-modified-since", get_header_value("if-modified-since", Ctx)},
  301. {"user-agent", get_header_value("user-agent", Ctx)},
  302. {"x-http-method-override", get_header_value("x-http-method-override", Ctx)}|Acc],
  303. lists:filter(fun({_, undefined}) -> false; (_) -> true end, L).
  304. -spec ewgi_spec(ewgi_context()) -> ewgi_spec().
  305. ewgi_spec(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  306. ?GET_EWGI(request(Ctx)).
  307. -spec ewgi_spec(ewgi_spec(), ewgi_context()) -> ewgi_context().
  308. ewgi_spec(E, Ctx) when ?IS_EWGI_SPEC(E), ?IS_EWGI_CONTEXT(Ctx) ->
  309. request(?SET_EWGI(E, request(Ctx)), Ctx).
  310. -spec read_input(ewgi_context()) -> ewgi_ri_callback() | 'undefined'.
  311. read_input(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  312. ?GET_EWGI_READ_INPUT(ewgi_spec(Ctx)).
  313. -spec read_input(ewgi_ri_callback(), non_neg_integer(), ewgi_context()) -> ewgi_ri_callback() | 'undefined'.
  314. read_input(Callback, Length, Ctx) when ?IS_EWGI_CONTEXT(Ctx),
  315. is_function(Callback, 1),
  316. is_integer(Length),
  317. Length >= 0 ->
  318. case read_input(Ctx) of
  319. F when is_function(F, 2) ->
  320. F(Callback, Length);
  321. undefined ->
  322. undefined
  323. end.
  324. %% @spec read_input_string(non_neg_integer(), ewgi_context()) -> string() | {error, no_input}
  325. %% @doc Reads the client message body into a string from the EWGI context.
  326. -spec read_input_string(non_neg_integer(), ewgi_context()) -> [byte()] | {'error', 'no_input'}.
  327. read_input_string(L, Ctx) when is_integer(L), L >= 0, ?IS_EWGI_CONTEXT(Ctx) ->
  328. case read_input(read_input_string_cb([]), L, Ctx) of
  329. undefined ->
  330. {error, no_input};
  331. Iol ->
  332. Bin = iolist_to_binary(Iol),
  333. binary_to_list(Bin)
  334. end.
  335. -spec read_input_string_cb(list()) -> ewgi_ri_callback().
  336. read_input_string_cb(Acc) ->
  337. F = fun(eof) ->
  338. lists:reverse(Acc);
  339. ({data, B}) ->
  340. read_input_string_cb([B|Acc])
  341. end,
  342. F.
  343. write_error(Msg, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  344. F = ?GET_EWGI_WRITE_ERROR(ewgi_spec(Ctx)),
  345. F(Msg).
  346. url_scheme(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  347. ?GET_EWGI_URL_SCHEME(ewgi_spec(Ctx)).
  348. version(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  349. ?GET_EWGI_VERSION(ewgi_spec(Ctx)).
  350. get_all_data(Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  351. ?GET_EWGI_DATA(ewgi_spec(Ctx)).
  352. find_data(Key, Ctx) ->
  353. find_data(Key, Ctx, undefined).
  354. find_data(Key, Ctx, Default) when ?IS_EWGI_CONTEXT(Ctx) ->
  355. case gb_trees:lookup(Key, get_all_data(Ctx)) of
  356. {value, V} ->
  357. V;
  358. none ->
  359. Default
  360. end.
  361. store_data(Key, Val, Ctx) when ?IS_EWGI_CONTEXT(Ctx) ->
  362. D = gb_trees:enter(Key, Val, get_all_data(Ctx)),
  363. ewgi_spec(?SET_EWGI_DATA(D, ewgi_spec(Ctx)), Ctx).
  364. %%--------------------------------------------------------------------
  365. %% @spec parse_qs(string()|binary()) -> [proplist()]
  366. %%
  367. %% @doc Parse a query string. Calls parse_data to do the job.
  368. %% @end
  369. %%--------------------------------------------------------------------
  370. parse_qs(ToParse) ->
  371. parse_data(ToParse, "ISO-8859-1").
  372. %%--------------------------------------------------------------------
  373. %% @spec parse_post(string()|binary()) -> [proplist()]
  374. %%
  375. %% @doc Parse application/x-www-form-urlencoded data.
  376. %% Calls parse_data to do the job.
  377. %% @end
  378. %%--------------------------------------------------------------------
  379. parse_post(ToParse) ->
  380. parse_data(ToParse).
  381. %%--------------------------------------------------------------------
  382. %% @spec parse_data(string()|binary()) -> [proplist()]
  383. %%
  384. %% @doc Parse a query string or application/x-www-form-urlencoded data.
  385. %% @end
  386. %%--------------------------------------------------------------------
  387. parse_data(undefined) ->
  388. [];
  389. parse_data(Binary) when is_binary(Binary) ->
  390. parse_data(binary_to_list(Binary), []);
  391. parse_data(String) ->
  392. parse_data(String, []).
  393. parse_data([], Acc) ->
  394. lists:reverse(Acc);
  395. parse_data(String, Acc) ->
  396. {{Key, Val}, Rest} = parse_kv(String),
  397. parse_data(Rest, [{Key, Val} | Acc]).
  398. %%--------------------------------------------------------------------
  399. %% @spec urlencode(proplist()) -> string()
  400. %%
  401. %% @doc URL encodes a proplist of parameters.
  402. %% @end
  403. %%--------------------------------------------------------------------
  404. -spec urlencode(ewgi_proplist()) -> string().
  405. urlencode(Props) ->
  406. QuotedL = [[quote(K), $=, quote(V)] || {K, V} <- Props],
  407. lists:flatten(join(QuotedL, $&)).
  408. %%--------------------------------------------------------------------
  409. %% @spec quote(term()) -> string()
  410. %%
  411. %% @doc URL encodes the given term.
  412. %% @end
  413. %%--------------------------------------------------------------------
  414. -spec quote(ewgi_propval()) -> string().
  415. quote(Term) when is_atom(Term) ->
  416. quote(atom_to_list(Term));
  417. quote(Term) when is_integer(Term) ->
  418. quote(integer_to_list(Term));
  419. quote(Term) when is_binary(Term) ->
  420. quote(binary_to_list(Term));
  421. quote(Term) when is_list(Term) ->
  422. quote(Term, []).
  423. -spec quote(string(), string()) -> string().
  424. quote([], Acc) ->
  425. lists:reverse(Acc);
  426. %% low alpha chars
  427. quote([H|Rest], Acc) when H >= $a, H =< $z ->
  428. quote(Rest, [H|Acc]);
  429. %% hialpha chars
  430. quote([H|Rest], Acc) when H >= $A, H =< $Z ->
  431. quote(Rest, [H|Acc]);
  432. %% digit chars
  433. quote([H|Rest], Acc) when H >= $0, H =< $9 ->
  434. quote(Rest, [H|Acc]);
  435. %% safe chars
  436. quote([H|Rest], Acc) when H =:= $-; H=:=$.; H=:=$_; H=:=$~ ->
  437. quote(Rest, [H|Acc]);
  438. %% space
  439. quote([$\s|Rest], Acc) ->
  440. quote(Rest, [$+ | Acc]);
  441. %% other characters (convert to hex)
  442. quote([H|Rest], Acc) ->
  443. <<Hi:4, Lo:4>> = <<H>>,
  444. quote(Rest, [to_hex(Lo), to_hex(Hi), $\% | Acc]).
  445. %%====================================================================
  446. %% Internal functions
  447. %%====================================================================
  448. %%--------------------------------------------------------------------
  449. %% @spec parse_kv(String::string()) -> parsed()|{error, Reason}
  450. %%
  451. %% @type parsed() = {ok, proplist(), Rest::string()}
  452. %%
  453. %% @doc Parser for kv pairs found in query strings and body data.
  454. %% returns the first proplist parsed from String or an error.
  455. %% @end
  456. %%--------------------------------------------------------------------
  457. -spec parse_kv(string()) -> {{string(), string()}, string()}.
  458. parse_kv(String) ->
  459. P = and_parser([until(fun is_equal/1), until(fun is_amp/1)]),
  460. {ok, [K, V], Rest} = P(String),
  461. {{unquote(K), unquote(V)}, Rest}.
  462. %%--------------------------------------------------------------------
  463. %% @spec and_parser(Rules::rules()) -> parsed()|{error, Reason}
  464. %%
  465. %% @type rules() = [rule()]
  466. %% rule() = function(template()).
  467. %%
  468. %% @doc and_parser of Rules.
  469. %% Applies each Rule in sequence to the Template passed.
  470. %% If a rule fails returns an error.
  471. %% @end
  472. %%--------------------------------------------------------------------
  473. -type parser() :: fun((list()) -> {'ok', list(), list()}).
  474. -spec and_parser([parser()]) -> parser().
  475. and_parser(Rules) ->
  476. fun(Tmpl) ->
  477. and_parser(Rules, Tmpl, [])
  478. end.
  479. -spec and_parser(list(), list(), list()) -> {'ok', list(), list()}.
  480. and_parser([], Tmpl, SoFar) ->
  481. {ok, lists:reverse(SoFar), Tmpl};
  482. and_parser([Rule|T], Tmpl, SoFar) ->
  483. {ok, Tok, Rest} = Rule(Tmpl),
  484. and_parser(T, Rest, [Tok|SoFar]).
  485. %%--------------------------------------------------------------------
  486. %% @spec until(predicate()) -> parsed()|{error, Reason}
  487. %%
  488. %% @type predicate() = function(template()).
  489. %%
  490. %% @doc until predicate P:
  491. %% output what it gets until P(H) is true.
  492. %% @end
  493. %%--------------------------------------------------------------------
  494. -type predicate() :: fun((list()) -> {'true', list()} | 'false').
  495. -spec until(predicate()) -> parser().
  496. until(P) ->
  497. fun (Tmpl) -> until(P, Tmpl, []) end.
  498. -spec until(predicate(), list(), list()) -> {'ok', list(), list()}.
  499. until(_P, [], Parsed) -> %% end of string so end parsing
  500. {ok, lists:reverse(Parsed), []};
  501. until(P, String, Parsed) ->
  502. case P(String) of
  503. {true, Rest} ->
  504. {ok, lists:reverse(Parsed), Rest};
  505. _ ->
  506. [H|Rest] = String,
  507. until(P, Rest, [H|Parsed])
  508. end.
  509. %%--------------------------------------------------------------------
  510. %% @spec is_equal(string()) -> boolean()
  511. %%
  512. %% @doc Match = character at the head of string.
  513. %% @end
  514. %%--------------------------------------------------------------------
  515. -spec is_equal(string()) -> boolean().
  516. is_equal([$=|Rest]) ->
  517. {true, Rest};
  518. is_equal(_) ->
  519. false.
  520. %%--------------------------------------------------------------------
  521. %% @spec is_amp(string()) -> boolean()
  522. %%
  523. %% @doc Match &amp; character or &amp;amp; entity at the beginning of string.
  524. %% @end
  525. %%--------------------------------------------------------------------
  526. -spec is_amp(string()) -> boolean().
  527. is_amp("&amp;"++Rest) ->
  528. {true, Rest};
  529. is_amp([$&|Rest]) ->
  530. {true, Rest};
  531. is_amp(_) ->
  532. false.
  533. %%--------------------------------------------------------------------
  534. %% @spec unquote(string()) -> string()
  535. %%
  536. %% @doc URL decodes the given term.
  537. %% Used to parse query strings and application/x-www-form-urlencoded data.
  538. %% @end
  539. %%--------------------------------------------------------------------
  540. unquote(Val) when is_binary(Val) ->
  541. unquote(binary_to_list(Val), []);
  542. unquote(Val) ->
  543. unquote(Val, []).
  544. unquote([], Acc) ->
  545. lists:reverse(Acc);
  546. unquote([37, Hi, Lo|Rest], Acc) -> % match %Hex
  547. unquote(Rest, [(from_hex(Lo) bor (from_hex(Hi) bsl 4))|Acc]);
  548. unquote([$+|Rest], Acc) ->
  549. unquote(Rest, [$\s|Acc]);
  550. unquote([H|Rest], Acc) ->
  551. unquote(Rest, [H|Acc]).
  552. %%--------------------------------------------------------------------
  553. %% @spec to_hex(char()) -> hex()
  554. %%
  555. %% @doc convert char to hex code.
  556. %% @end
  557. %%--------------------------------------------------------------------
  558. -spec to_hex(0..16) -> byte().
  559. to_hex(C) when C >= 0, C < 10 -> $0 + C;
  560. to_hex(C) when C >= 0, C < 16 -> $A + (C - 10).
  561. %%--------------------------------------------------------------------
  562. %% @spec from_hex(hex()) -> char()
  563. %%
  564. %% @doc Used to get char from hex code.
  565. %% @end
  566. %%--------------------------------------------------------------------
  567. -spec from_hex(byte()) -> 0..16.
  568. from_hex(C) when C >= $0, C =< $9 -> C - $0;
  569. from_hex(C) when C >= $a, C =< $f -> C - $a + 10;
  570. from_hex(C) when C >= $A, C =< $F -> C - $A + 10.
  571. %%--------------------------------------------------------------------
  572. %% @spec join([string()], Sep::string()) -> string()
  573. %%
  574. %% @doc Joins a list of elements using a separator.
  575. %% The result is reversed for efficiency.
  576. %% @end
  577. %%--------------------------------------------------------------------
  578. -spec join([string()], string() | char()) -> string().
  579. join(Strings, Sep) ->
  580. join(Strings, Sep, []).
  581. -spec join([string()], string() | char(), list()) -> string().
  582. join([], _Sep, _Acc) ->
  583. [];
  584. join([Last], _Sep, Acc) ->
  585. [Last|Acc];
  586. join([H|Rest], Sep, Acc) ->
  587. join(Rest, Sep, [Sep, H|Acc]).
  588. -spec nhdr(atom() | binary() | string()) -> string().
  589. nhdr(L) when is_atom(L) ->
  590. nhdr(atom_to_list(L));
  591. nhdr(L) when is_binary(L) ->
  592. nhdr(binary_to_list(L));
  593. nhdr(L) when is_list(L) ->
  594. string:strip(string:to_lower(L)).
  595. normalize_header({K, V}) ->
  596. {nhdr(K), string:strip(V)}.
  597. %% http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
  598. %% and
  599. %% http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2
  600. unquote_path(Path) ->
  601. PathComponents = [unquote(X) || X <- path_components(Path, [], [])],
  602. lists:flatten(join(PathComponents, "%2F")).
  603. path_components([], Piece, Acc) ->
  604. [lists:reverse(Piece)|Acc];
  605. path_components("%2f" ++ Rest, Piece, Acc) ->
  606. path_components(Rest, [], [lists:reverse(Piece)|Acc]);
  607. path_components("%2F" ++ Rest, Piece, Acc) ->
  608. path_components(Rest, [], [lists:reverse(Piece)|Acc]);
  609. path_components([C|Rest], Piece, Acc) ->
  610. path_components(Rest, [C|Piece], Acc).
  611. -define(EWGI_SPEC_FIELDS, [{read_input, fun(E, V) -> ?SET_EWGI_READ_INPUT(V, E) end},
  612. {write_error, fun(E, V) -> ?SET_EWGI_WRITE_ERROR(V, E) end},
  613. {url_scheme, fun(E, V) -> ?SET_EWGI_URL_SCHEME(V, E) end},
  614. {version, fun(E, V) -> ?SET_EWGI_VERSION(V, E) end},
  615. {data, fun(E, V) -> ?SET_EWGI_DATA(V, E) end}]).
  616. -define(EWGI_HTTP_HEADER_FIELDS, [{http_accept, fun(E, V) -> ?SET_HTTP_ACCEPT(V, E) end},
  617. {http_cookie, fun(E, V) -> ?SET_HTTP_COOKIE(V, E) end},
  618. {http_host, fun(E, V) -> ?SET_HTTP_HOST(V, E) end},
  619. {http_if_modified_since, fun(E, V) -> ?SET_HTTP_IF_MODIFIED_SINCE(V, E) end},
  620. {http_user_agent, fun(E, V) -> ?SET_HTTP_USER_AGENT(V, E) end},
  621. {http_x_http_method_override, fun(E, V) -> ?SET_HTTP_X_HTTP_METHOD_OVERRIDE(V, E) end},
  622. {other, fun(E, V) -> ?SET_HTTP_OTHER(V, E) end}]).
  623. -define(EWGI_REQUEST_FIELDS, [{auth_type, fun(E, V) -> ?SET_AUTH_TYPE(V, E) end},
  624. {content_length, fun(E, V) -> ?SET_CONTENT_LENGTH(V, E) end},
  625. {content_type, fun(E, V) -> ?SET_CONTENT_TYPE(V, E) end},
  626. {ewgi, fun(E, V) -> ?SET_EWGI(V, E) end},
  627. {gateway_interface, fun(E, V) -> ?SET_GATEWAY_INTERFACE(V, E) end},
  628. {http_headers, fun(E, V) -> ?SET_HTTP_HEADERS(V, E) end},
  629. {path_info, fun(E, V) -> ?SET_PATH_INFO(V, E) end},
  630. {path_translated, fun(E, V) -> ?SET_PATH_TRANSLATED(V, E) end},
  631. {query_string, fun(E, V) -> ?SET_QUERY_STRING(V, E) end},
  632. {remote_addr, fun(E, V) -> ?SET_REMOTE_ADDR(V, E) end},
  633. {remote_host, fun(E, V) -> ?SET_REMOTE_HOST(V, E) end},
  634. {remote_ident, fun(E, V) -> ?SET_REMOTE_IDENT(V, E) end},
  635. {remote_user, fun(E, V) -> ?SET_REMOTE_USER(V, E) end},
  636. {remote_user_data, fun(E, V) -> ?SET_REMOTE_USER_DATA(V, E) end},
  637. {request_method, fun(E, V) -> ?SET_REQUEST_METHOD(V, E) end},
  638. {script_name, fun(E, V) -> ?SET_SCRIPT_NAME(V, E) end},
  639. {server_name, fun(E, V) -> ?SET_SERVER_NAME(V, E) end},
  640. {server_port, fun(E, V) -> ?SET_SERVER_PORT(V, E) end},
  641. {server_protocol, fun(E, V) -> ?SET_SERVER_PROTOCOL(V, E) end},
  642. {server_software, fun(E, V) -> ?SET_SERVER_SOFTWARE(V, E) end}]).
  643. empty_ewgi_spec() ->
  644. {'ewgi_spec', undefined, undefined, undefined, undefined,
  645. gb_trees:empty()}.
  646. empty_http_headers() ->
  647. {'ewgi_http_headers', undefined, undefined, undefined, undefined,
  648. undefined, undefined, gb_trees:empty()}.
  649. server_request_foldl(Req0, ParseFun0, ParseEwgiFun, ParseHttpFun) ->
  650. ParseFun = fun(ewgi, Req) ->
  651. request_foldl(Req, ParseEwgiFun, empty_ewgi_spec(), ?EWGI_SPEC_FIELDS);
  652. (http_headers, Req) ->
  653. request_foldl(Req, ParseHttpFun, empty_http_headers(), ?EWGI_HTTP_HEADER_FIELDS);
  654. (Field, Req) ->
  655. ParseFun0(Field, Req)
  656. end,
  657. request_foldl(Req0, ParseFun, empty_request(), ?EWGI_REQUEST_FIELDS).
  658. request_foldl(Req, ParseFun, EmptyRec, Fields) ->
  659. lists:foldl(fun({Field, F}, Rec) ->
  660. case ParseFun(Field, Req) of
  661. undefined ->
  662. Rec;
  663. V ->
  664. F(Rec, V)
  665. end
  666. end, EmptyRec, Fields).
  667. t_lookup_default(K, T, Default) ->
  668. case gb_trees:lookup(K, T) of
  669. {value, V} ->
  670. V;
  671. none ->
  672. Default
  673. end.
  674. t_insert_header(K, Pair, T) ->
  675. gb_trees:enter(K, lists:reverse([Pair|lists:reverse(t_lookup_default(K, T, []))]), T).
  676. t_enter_header(K, Pair, T) ->
  677. gb_trees:enter(K, Pair, T).
  678. %% The following method (urlsplit/1) is taken from the MochiWeb
  679. %% project module mochiweb_util.
  680. %% Copyright 2007 MochiMedia, Inc.
  681. %% See LICENSE for more details.
  682. %% @spec urlsplit(Url) -> {Scheme, Netloc, Path, Query, Fragment}
  683. %% @doc Return a 5-tuple, does not expand % escapes. Only supports HTTP style
  684. %% URLs.
  685. urlsplit(Url) ->
  686. {Scheme, Url1} = urlsplit_scheme(Url),
  687. {Netloc, Url2} = urlsplit_netloc(Url1),
  688. {Path, Query, Fragment} = urlsplit_path(Url2),
  689. {Scheme, Netloc, Path, Query, Fragment}.
  690. urlsplit_scheme(Url) ->
  691. urlsplit_scheme(Url, []).
  692. urlsplit_scheme([], Acc) ->
  693. {"", lists:reverse(Acc)};
  694. urlsplit_scheme(":" ++ Rest, Acc) ->
  695. {string:to_lower(lists:reverse(Acc)), Rest};
  696. urlsplit_scheme([C | Rest], Acc) ->
  697. urlsplit_scheme(Rest, [C | Acc]).
  698. urlsplit_netloc("//" ++ Rest) ->
  699. urlsplit_netloc(Rest, []);
  700. urlsplit_netloc(Path) ->
  701. {"", Path}.
  702. urlsplit_netloc(Rest=[C | _], Acc) when C =:= $/; C =:= $?; C =:= $# ->
  703. {lists:reverse(Acc), Rest};
  704. urlsplit_netloc([C | Rest], Acc) ->
  705. urlsplit_netloc(Rest, [C | Acc]).
  706. %% @spec urlsplit_path(Url) -> {Path, Query, Fragment}
  707. %% @doc Return a 3-tuple, does not expand % escapes. Only supports HTTP style
  708. %% paths.
  709. urlsplit_path(Path) ->
  710. urlsplit_path(Path, []).
  711. urlsplit_path("", Acc) ->
  712. {lists:reverse(Acc), "", ""};
  713. urlsplit_path("?" ++ Rest, Acc) ->
  714. {Query, Fragment} = urlsplit_query(Rest),
  715. {lists:reverse(Acc), Query, Fragment};
  716. urlsplit_path("#" ++ Rest, Acc) ->
  717. {lists:reverse(Acc), "", Rest};
  718. urlsplit_path([C | Rest], Acc) ->
  719. urlsplit_path(Rest, [C | Acc]).
  720. urlsplit_query(Query) ->
  721. urlsplit_query(Query, []).
  722. urlsplit_query("", Acc) ->
  723. {lists:reverse(Acc), ""};
  724. urlsplit_query("#" ++ Rest, Acc) ->
  725. {lists:reverse(Acc), Rest};
  726. urlsplit_query([C | Rest], Acc) ->
  727. urlsplit_query(Rest, [C | Acc]).
  728. %%--------------------------------------------------------------------
  729. %% Stream methods
  730. %%--------------------------------------------------------------------
  731. %% chunked response
  732. stream_process_init(Ctx, chunked) when ?IS_EWGI_CONTEXT(Ctx) ->
  733. {StatusCode, _} = ewgi_api:response_status(Ctx),
  734. Headers = ewgi_api:response_headers(Ctx),
  735. ChunkedHeader = {"Transfer-Encoding", "chunked"},
  736. wait_for_socket(StatusCode, [ChunkedHeader|Headers], chunked);
  737. %% non chunked response
  738. stream_process_init(Ctx, CL) when ?IS_EWGI_CONTEXT(Ctx), is_integer(CL) ->
  739. {StatusCode, _} = ewgi_api:response_status(Ctx),
  740. Headers = ewgi_api:response_headers(Ctx),
  741. CLHeader = {"Content-Length", integer_to_list(CL)},
  742. wait_for_socket(StatusCode, [CLHeader|Headers], non_chunked).
  743. %% This API is for processes that don't have access the original ewgi_context()
  744. stream_process_init(StatusCode, Headers, chunked) ->
  745. ChunkedHeader = {"Transfer-Encoding", "chunked"},
  746. wait_for_socket(StatusCode, [ChunkedHeader|Headers], chunked);
  747. stream_process_init(StatusCode, Headers, CL) when is_integer(CL) ->
  748. CLHeader = {"Content-Length", integer_to_list(CL)},
  749. wait_for_socket(StatusCode, [CLHeader|Headers], non_chunked).
  750. -define(STREAM_INIT_TIMEOUT, 5000).
  751. wait_for_socket(StatusCode, Headers, TransferEncoding) ->
  752. receive
  753. {push_stream_init, ServerModule, ServerPid, Socket} ->
  754. ServerPid ! {push_stream_init, self(), StatusCode, Headers, TransferEncoding},
  755. Connection = {ServerModule, ServerPid, Socket},
  756. %% The server should report back on whether we should send data.
  757. %% Sometimes (Method='HEAD') only the headers are sent.
  758. receive
  759. {ok, ServerPid} ->
  760. {ok, Connection};
  761. {discard, ServerPid} ->
  762. stream_process_end(Connection),
  763. discard
  764. end
  765. after ?STREAM_INIT_TIMEOUT ->
  766. error_logger:error_msg(?MODULE_STRING ++": Timeout while trying to init stream process!~n"),
  767. discard
  768. end.
  769. stream_process_deliver({ServerModule, _ServerPid, Socket}, IoList) ->
  770. ServerModule:stream_process_deliver(Socket, IoList).
  771. stream_process_deliver_chunk({ServerModule, _ServerPid, Socket}, IoList) ->
  772. ServerModule:stream_process_deliver_chunk(Socket, IoList).
  773. stream_process_deliver_final_chunk({ServerModule, _ServerPid, Socket}, IoList) ->
  774. ServerModule:stream_process_deliver_final_chunk(Socket, IoList).
  775. stream_process_end({ServerModule, ServerPid, Socket}) ->
  776. ServerModule:stream_process_end(Socket, ServerPid).