Erlang-Parser
view release on metacpan or search on metacpan
H1 = ?MODULE:insert(taco, grande, H),
[{hdr, "foo, bar, 2"}, {taco, "grande"}] = ?MODULE:to_list(H1),
H2 = ?MODULE:make([{"Set-Cookie", "foo"}]),
[{"Set-Cookie", "foo"}] = ?MODULE:to_list(H2),
H3 = ?MODULE:insert("Set-Cookie", "bar", H2),
[{"Set-Cookie", "foo"}, {"Set-Cookie", "bar"}] = ?MODULE:to_list(H3),
"foo, bar" = ?MODULE:get_value("set-cookie", H3),
{value, {"Set-Cookie", "foo, bar"}} = ?MODULE:lookup("set-cookie", H3),
undefined = ?MODULE:get_value("shibby", H3),
none = ?MODULE:lookup("shibby", H3),
H4 = ?MODULE:insert("content-type",
"application/x-www-form-urlencoded; charset=utf8",
H3),
"application/x-www-form-urlencoded" = ?MODULE:get_primary_value(
"content-type", H4),
H4 = ?MODULE:delete_any("nonexistent-header", H4),
H3 = ?MODULE:delete_any("content-type", H4),
HB = <<"Content-Length: 47\r\nContent-Type: text/plain\r\n\r\n">>,
H_HB = ?MODULE:from_binary(HB),
H_HB = ?MODULE:from_binary(binary_to_list(HB)),
"47" = ?MODULE:get_value("Content-Length", H_HB),
"text/plain" = ?MODULE:get_value("Content-Type", H_HB),
L_H_HB = ?MODULE:to_list(H_HB),
2 = length(L_H_HB),
true = lists:member({'Content-Length', "47"}, L_H_HB),
true = lists:member({'Content-Type', "text/plain"}, L_H_HB),
HL = [ <<"Content-Length: 47\r\n">>, <<"Content-Type: text/plain\r\n">> ],
parse_form_outer({headers, H}, FileHandler, State) ->
{"form-data", H1} = proplists:get_value("content-disposition", H),
Name = proplists:get_value("name", H1),
Filename = proplists:get_value("filename", H1),
case Filename of
undefined ->
fun (Next) ->
parse_form_value(Next, {Name, []}, FileHandler, State)
end;
_ ->
ContentType = proplists:get_value("content-type", H),
Handler = FileHandler(Filename, ContentType),
fun (Next) ->
parse_form_file(Next, {Name, Handler}, FileHandler, State)
end
end.
parse_form_value(body_end, {Name, Acc}, FileHandler, State) ->
Value = binary_to_list(iolist_to_binary(lists:reverse(Acc))),
State1 = [{Name, Value} | State],
fun (Next) -> parse_form_outer(Next, FileHandler, State1) end;
Value = iolist_to_binary(lists:reverse(Acc)),
{Filename, ContentType, Value};
(Next) ->
default_file_handler_1(Filename, ContentType, [Next | Acc])
end.
parse_multipart_request(Req, Callback) ->
%% TODO: Support chunked?
Length = list_to_integer(Req:get_header_value("content-length")),
Boundary = iolist_to_binary(
get_boundary(Req:get_header_value("content-type"))),
Prefix = <<"\r\n--", Boundary/binary>>,
BS = byte_size(Boundary),
Chunk = read_chunk(Req, Length),
Length1 = Length - byte_size(Chunk),
<<"--", Boundary:BS/binary, "\r\n", Rest/binary>> = Chunk,
feed_mp(headers, flash_multipart_hack(#mp{boundary=Prefix,
length=Length1,
buffer=Rest,
callback=Callback,
req=Req})).
Res = (catch ClientFun(Client)),
mochiweb_socket_server:stop(Server),
Res.
fake_request(Socket, ContentType, Length) ->
mochiweb_request:new(Socket,
'POST',
"/multipart",
{1,1},
mochiweb_headers:make(
[{"content-type", ContentType},
{"content-length", Length}])).
test_callback({body, <<>>}, Rest=[body_end | _]) ->
%% When expecting the body_end we might get an empty binary
fun (Next) -> test_callback(Next, Rest) end;
test_callback({body, Got}, [{body, Expect} | Rest]) when Got =/= Expect ->
%% Partial response
GotSize = size(Got),
<<Got:GotSize/binary, Expect1/binary>> = Expect,
fun (Next) -> test_callback(Next, [{body, Expect1} | Rest]) end;
ContentType = "multipart/form-data; boundary=---------------------------7386909285754635891697677882",
BinContent = <<"-----------------------------7386909285754635891697677882\r\nContent-Disposition: form-data; name=\"hidden\"\r\n\r\nmultipart message\r\n-----------------------------7386909285754635891697677882\r\nContent-Disposition: form-data; ...
Expect = [{headers,
[{"content-disposition",
{"form-data", [{"name", "hidden"}]}}]},
{body, <<"multipart message">>},
body_end,
{headers,
[{"content-disposition",
{"form-data", [{"name", "file"}, {"filename", "test_file.txt"}]}},
{"content-type", {"text/plain", []}}]},
{body, <<"Woo multiline text file\n\nLa la la">>},
body_end,
eof],
TestCallback = fun (Next) -> test_callback(Next, Expect) end,
ServerFun = fun (Socket) ->
ok = mochiweb_socket:send(Socket, BinContent),
exit(normal)
end,
ClientFun = fun (Socket) ->
Req = fake_request(Socket, ContentType,
ContentType = "multipart/form-data; boundary=---------------------------6072231407570234361599764024",
BinContent = <<"-----------------------------6072231407570234361599764024\r\nContent-Disposition: form-data; name=\"hidden\"\r\n\r\nmultipart message\r\n-----------------------------6072231407570234361599764024\r\nContent-Disposition: form-data; ...
Expect = [{headers,
[{"content-disposition",
{"form-data", [{"name", "hidden"}]}}]},
{body, <<"multipart message">>},
body_end,
{headers,
[{"content-disposition",
{"form-data", [{"name", "file"}, {"filename", ""}]}},
{"content-type", {"application/octet-stream", []}}]},
{body, <<>>},
body_end,
eof],
TestCallback = fun (Next) -> test_callback(Next, Expect) end,
ServerFun = fun (Socket) ->
ok = mochiweb_socket:send(Socket, BinContent),
exit(normal)
end,
ClientFun = fun (Socket) ->
Req = fake_request(Socket, ContentType,
""], "\r\n"),
BinContent = iolist_to_binary(Content),
Expect = [{headers,
[{"content-disposition",
{"form-data", [{"name", "submit-name"}]}}]},
{body, <<"Larry">>},
body_end,
{headers,
[{"content-disposition",
{"form-data", [{"name", "files"}, {"filename", "file1.txt"}]}},
{"content-type", {"text/plain", []}}]},
{body, <<"... contents of file1.txt ...">>},
body_end,
eof],
TestCallback = fun (Next) -> test_callback(Next, Expect) end,
ServerFun = fun (Socket) ->
ok = mochiweb_socket:send(Socket, BinContent),
exit(normal)
end,
ClientFun = fun (Socket) ->
Req = fake_request(Socket, ContentType,
""], "\r\n"),
BinContent = iolist_to_binary(Content),
Expect = [{headers,
[{"content-disposition",
{"form-data", [{"name", "submit-name"}]}}]},
{body, <<"Larry">>},
body_end,
{headers,
[{"content-disposition",
{"form-data", [{"name", "files"}, {"filename", "file1.txt"}]}},
{"content-type", {"text/plain", []}}
]},
{body, <<"... contents of file1.txt ...">>},
body_end,
eof],
TestCallback = fun (Next) -> test_callback(Next, Expect) end,
ServerFun = fun (Socket) ->
ok = mochiweb_socket:send(Socket, BinContent),
exit(normal)
end,
ClientFun = fun (Socket) ->
""], "\r\n"),
BinContent = iolist_to_binary(Content),
Expect = [{headers,
[{"content-disposition",
{"form-data", [{"name", "submit-name"}]}}]},
{body, <<"Larry">>},
body_end,
{headers,
[{"content-disposition",
{"form-data", [{"name", "files"}, {"filename", "file1.txt"}]}},
{"content-type", {"text/plain", []}},
{"x-large-header", {string:copies("%", 4096), []}}
]},
{body, <<"... contents of file1.txt ...">>},
body_end,
eof],
TestCallback = fun (Next) -> test_callback(Next, Expect) end,
ServerFun = fun (Socket) ->
ok = mochiweb_socket:send(Socket, BinContent),
exit(normal)
end,
{body, <<"hello.txt">>},
body_end,
{headers,
[{"content-disposition",
{"form-data", [{"name", "success_action_status"}]}}]},
{body, <<"201">>},
body_end,
{headers,
[{"content-disposition",
{"form-data", [{"name", "file"}, {"filename", "hello.txt"}]}},
{"content-type", {"application/octet-stream", []}}]},
{body, <<"hello\n">>},
body_end,
{headers,
[{"content-disposition",
{"form-data", [{"name", "Upload"}]}}]},
{body, <<"Submit Query">>},
body_end,
eof],
TestCallback = fun (Next) -> test_callback(Next, Expect) end,
ServerFun = fun (Socket) ->
{body, <<"hello.txt">>},
body_end,
{headers,
[{"content-disposition",
{"form-data", [{"name", "success_action_status"}]}}]},
{body, <<"201">>},
body_end,
{headers,
[{"content-disposition",
{"form-data", [{"name", "file"}, {"filename", "hello.txt"}]}},
{"content-type", {"application/octet-stream", []}}]},
{body, Chunk},
body_end,
{headers,
[{"content-disposition",
{"form-data", [{"name", "Upload"}]}}]},
{body, <<"Submit Query">>},
body_end,
eof],
TestCallback = fun (Next) -> test_callback(Next, Expect) end,
ServerFun = fun (Socket) ->
{body, <<"hello.txt">>},
body_end,
{headers,
[{"content-disposition",
{"form-data", [{"name", "success_action_status"}]}}]},
{body, <<"201">>},
body_end,
{headers,
[{"content-disposition",
{"form-data", [{"name", "file"}, {"filename", "hello.txt"}]}},
{"content-type", {"application/octet-stream", []}}]},
{body, Chunk},
body_end,
{headers,
[{"content-disposition",
{"form-data", [{"name", "Upload"}]}}]},
{body, <<"Submit Query">>},
body_end,
eof],
TestCallback = fun (Next) -> test_callback(Next, Expect) end,
ServerFun = fun (Socket) ->
%% @spec parse_post() -> [{Key::string(), Value::string()}]
%% @doc Parse an application/x-www-form-urlencoded form POST. This
%% has the side-effect of calling recv_body().
parse_post() ->
case erlang:get(?SAVE_POST) of
undefined ->
Parsed = case recv_body() of
undefined ->
[];
Binary ->
case get_primary_header_value("content-type") of
"application/x-www-form-urlencoded" ++ _ ->
mochiweb_util:parse_qs(Binary);
_ ->
[]
end
end,
put(?SAVE_POST, Parsed),
Parsed;
Cached ->
Cached
( run in 1.620 second using v1.01-cache-2.11-cpan-d7f47b0818f )