PageRenderTime 23ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/src/middleware/ewgi_post/ewgi_post.erl

http://github.com/skarab/ewgi
Erlang | 171 lines | 130 code | 15 blank | 26 comment | 2 complexity | 07b73cdc4a10b79e0e06c4e608290493 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. %% @author Filippo Pacini <filippo.pacini@gmail.com>
  2. %% @copyright 2009 S.G. Consulting.
  3. %% @doc Post example
  4. -module(ewgi_post).
  5. -author('Filippo Pacini <filippo.pacini@gmail.com>').
  6. -export([run/2]).
  7. -export([post_app_example/1]).
  8. -define(DEFAULT_MAX_LENGTH, 2097152). %% 2 MB maximum
  9. run(Ctx, [NoPostApp, FailedPostApp, SuccessPostApp]) ->
  10. run(Ctx, [NoPostApp, FailedPostApp, SuccessPostApp, ?DEFAULT_MAX_LENGTH]);
  11. run(Ctx, [NoPostApp, FailedPostApp, SuccessPostApp, MaxLength]) ->
  12. Parser = post_parse_middleware(MaxLength, SuccessPostApp, FailedPostApp),
  13. case ewgi_api:request_method(Ctx) of
  14. 'GET' ->
  15. NoPostApp(Ctx);
  16. 'POST' ->
  17. Parser(Ctx)
  18. end.
  19. %% MaxLength is the maximum size (in bytes) that the server will
  20. %% receive from the client. App should be the application called when
  21. %% the parse is successful (or unnecessary). ErrApp should be an
  22. %% error application when the content length exceeds the maximum
  23. %% specified limit.
  24. post_parse_middleware(MaxLength, App, ErrApp)
  25. when is_integer(MaxLength), MaxLength > 0, is_function(App, 1) ->
  26. fun(Ctx) ->
  27. case ewgi_api:request_method(Ctx) of
  28. Method when Method =:= 'POST';
  29. Method =:= 'PUT' ->
  30. case ewgi_api:remote_user_data(Ctx) of
  31. undefined ->
  32. %% Check content-type first
  33. Ct = ewgi_api:content_type(Ctx),
  34. parse_post(Ctx, App, ErrApp, parse_ct(Ct), MaxLength);
  35. _ ->
  36. App(Ctx)
  37. end;
  38. _ ->
  39. App(Ctx)
  40. end
  41. end.
  42. %% Parse content-type (ignoring additional vars for now)
  43. %% Should look like "major/minor; var=val"
  44. parse_ct(L) when is_list(L) ->
  45. case string:tokens(L, ";") of
  46. [CT|Vars] ->
  47. Vars1 = [string:tokens(VarStr, "=") || VarStr <- Vars],
  48. Vars2 = [{string:strip(Name), Value} || [Name, Value] <- Vars1],
  49. {CT, Vars2};
  50. _ ->
  51. undefined
  52. end.
  53. parse_post(Ctx, App, ErrApp, {"application/x-www-form-urlencoded", Vars}, Max) ->
  54. case ewgi_api:content_length(Ctx) of
  55. L when is_integer(L), L > Max ->
  56. %% shouldn't we set an error message here?
  57. ErrApp(Ctx);
  58. L when is_integer(L), L > 0 ->
  59. Input = read_input_string(Ctx, L),
  60. %% http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html
  61. %% When no explicit charset parameter is provided by the sender,
  62. %% media subtypes of the "text" type are defined to have a default
  63. %% charset value of "ISO-8859-1" when received via HTTP.
  64. case proplists:get_value("charset", Vars) of
  65. undefined -> InCharset = "iso-8859-1"
  66. ;Charset -> InCharset = string:to_lower(Charset)
  67. end,
  68. UnicodeInput = to_unicode(Input, InCharset),
  69. Vals = ewgi_api:parse_post(UnicodeInput),
  70. Ctx1 = ewgi_api:remote_user_data(Vals, Ctx),
  71. App(Ctx1);
  72. _ ->
  73. ErrApp(Ctx)
  74. end;
  75. parse_post(Ctx, App, ErrApp, {"application/json", _Vars}, Max) ->
  76. case ewgi_api:content_length(Ctx) of
  77. L when is_integer(L), L > Max ->
  78. %% shouldn't we set an error message here?
  79. ErrApp(Ctx);
  80. L when is_integer(L), L > 0 ->
  81. Input = read_input_string(Ctx, L),
  82. %% http://www.ietf.org/rfc/rfc4627.txt
  83. %% JSON text SHALL be encoded in Unicode.
  84. %% The default encoding is UTF-8.
  85. case unicode:bom_to_encoding(Input) of
  86. {latin1,0} -> InEncoding = utf8
  87. ;{InEncoding, _Length} -> ok
  88. end,
  89. UnicodeInput = unicode:characters_to_list(Input, InEncoding),
  90. {Json, [], _} = ktj_decode:decode(UnicodeInput),
  91. Vals = [{"json", Json}],
  92. Ctx1 = ewgi_api:remote_user_data(Vals, Ctx),
  93. App(Ctx1);
  94. _ ->
  95. ErrApp(Ctx)
  96. end;
  97. parse_post(Ctx, App, _, _, _) ->
  98. %% Silently ignore other content-types
  99. App(Ctx).
  100. read_input_string(Ctx, L) when is_integer(L), L > 0 ->
  101. R = ewgi_api:read_input(Ctx),
  102. iolist_to_binary(R(read_input_string_cb([]), L)).
  103. read_input_string_cb(Acc) ->
  104. fun(eof) ->
  105. lists:reverse(Acc);
  106. ({data, B}) ->
  107. read_input_string_cb([B|Acc])
  108. end.
  109. %% Transforms the data from the given charset to unicode
  110. %% Todo: add support for other charset as needed.
  111. to_unicode(Data, "iso-8859-1") ->
  112. unicode:characters_to_list(Data, latin1);
  113. to_unicode(Data, "utf8") ->
  114. unicode:characters_to_list(Data, utf8);
  115. to_unicode(Data, "utf-8") ->
  116. unicode:characters_to_list(Data, utf8).
  117. %%
  118. %% example functions on how to use the post handling middleware
  119. %%
  120. post_app_example(Ctx) ->
  121. run(Ctx, [fun display_form/1,
  122. fun post_app_error/1,
  123. fun display_form_data/1]).
  124. post_app_error({ewgi_context, Request, _}) ->
  125. Response = {ewgi_response, {400, "BAD REQUEST"}, [],
  126. [<<"Maximum content-length exceeded.">>],
  127. undefined},
  128. {ewgi_context, Request, Response}.
  129. display_form_data({ewgi_context, Request, _Response}=Ctx) ->
  130. Body =
  131. case ewgi_api:remote_user_data(Ctx) of
  132. undefined ->
  133. "undefined";
  134. Body1 ->
  135. io_lib:format("~p", [Body1])
  136. end,
  137. ResponseHeaders = [{"Content-type", "text/html; charset=utf8"}],
  138. Response = {ewgi_response,
  139. {200, "OK"},
  140. ResponseHeaders,
  141. [Body], undefined},
  142. {ewgi_context, Request, Response}.
  143. display_form({ewgi_context, Request, _Response}) ->
  144. Body = <<"<form action=\"/postex\" method=\"post\">
  145. Un: <input type=\"text\" name=\"un\" value=\"\"/>
  146. <br/>
  147. Pw: <input type=\"text\" name=\"pw\" value=\"\"/>
  148. <br/><br/>
  149. <input type=\"submit\" name=\"submit\" value=\"Login\"/>
  150. </form>">>,
  151. ResponseHeaders = [{"Content-type", "text/html"}],
  152. Response = {ewgi_response,
  153. {200, "OK"},
  154. ResponseHeaders,
  155. [Body], undefined},
  156. {ewgi_context, Request, Response}.