From 528507c7decb2bf2fcbb55a47256011c2ce4bd4b Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Tue, 8 Nov 2011 00:51:49 +0100 Subject: Add multipart support --- src/cowboy_http_req.erl | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) (limited to 'src/cowboy_http_req.erl') diff --git a/src/cowboy_http_req.erl b/src/cowboy_http_req.erl index fa4e620..f850e52 100644 --- a/src/cowboy_http_req.erl +++ b/src/cowboy_http_req.erl @@ -36,6 +36,10 @@ body/1, body/2, body_qs/1 ]). %% Request Body API. +-export([ + multipart_data/1, multipart_skip/1 +]). %% Request Multipart API. + -export([ reply/2, reply/3, reply/4, chunked_reply/2, chunked_reply/3, chunk/2, @@ -358,6 +362,69 @@ body_qs(Req) -> {ok, Body, Req2} = body(Req), {parse_qs(Body), Req2}. +%% Multipart Request API. + +%% @doc Return data from the multipart parser. +%% +%% Use this function for multipart streaming. For each part in the request, +%% this function returns {headers, Headers} followed by a sequence of +%% {data, Data} tuples and finally end_of_part. When there +%% is no part to parse anymore, eof is returned. +%% +%% If the request Content-Type is not a multipart one, {error, badarg} +%% is returned. +-spec multipart_data(#http_req{}) -> {multipart_data(), #http_req{}}. +multipart_data(Req=#http_req{body_state=waiting}) -> + {{<<"multipart">>, _SubType, Params}, Req2} = + parse_header('Content-Type', Req), + {_, Boundary} = lists:keyfind(<<"boundary">>, 1, Params), + {Length, Req3=#http_req{buffer=Buffer}} = + parse_header('Content-Length', Req2), + multipart_data(Req3, Length, cowboy_multipart:parser(Boundary), Buffer); +multipart_data(Req=#http_req{body_state={multipart, Length, Cont}}) -> + multipart_data(Req, Length, Cont()); +multipart_data(Req=#http_req{body_state=done}) -> + {eof, Req}. + +multipart_data(Req, Length, Parser, Buffer) when byte_size(Buffer) >= Length -> + << Data:Length/binary, Rest/binary >> = Buffer, + multipart_data(Req#http_req{buffer=Rest}, 0, Parser(Data)); +multipart_data(Req, Length, Parser, Buffer) -> + NewLength = Length - byte_size(Buffer), + multipart_data(Req#http_req{buffer= <<>>}, NewLength, Parser(Buffer)). + +multipart_data(Req, Length, {headers, Headers, Cont}) -> + {{headers, Headers}, Req#http_req{body_state={multipart, Length, Cont}}}; +multipart_data(Req, Length, {body, Data, Cont}) -> + {{body, Data}, Req#http_req{body_state={multipart, Length, Cont}}}; +multipart_data(Req, Length, {end_of_part, Cont}) -> + {end_of_part, Req#http_req{body_state={multipart, Length, Cont}}}; +multipart_data(Req, 0, eof) -> + {eof, Req#http_req{body_state=done}}; +multipart_data(Req=#http_req{socket=Socket, transport=Transport}, + Length, eof) -> + {ok, _Data} = Transport:recv(Socket, Length, 5000), + {eof, Req#http_req{body_state=done}}; +multipart_data(Req=#http_req{socket=Socket, transport=Transport}, + Length, {more, Parser}) when Length > 0 -> + case Transport:recv(Socket, 0, 5000) of + {ok, << Data:Length/binary, Buffer/binary >>} -> + multipart_data(Req#http_req{buffer=Buffer}, 0, Parser(Data)); + {ok, Data} -> + multipart_data(Req, Length - byte_size(Data), Parser(Data)) + end. + +%% @doc Skip a part returned by the multipart parser. +%% +%% This function repeatedly calls multipart_data/1 until +%% end_of_part or eof is parsed. +multipart_skip(Req) -> + case multipart_data(Req) of + {end_of_part, Req2} -> {ok, Req2}; + {eof, Req2} -> {ok, Req2}; + {_Other, Req2} -> multipart_skip(Req2) + end. + %% Response API. %% @equiv reply(Status, [], [], Req) -- cgit v1.2.3