Просмотр исходного кода

Ignore the boundary parameter when accepting multipart

Loïc Hoguin 6 лет назад
Родитель
Сommit
4fedb33631
3 измененных файлов с 61 добавлено и 20 удалено
  1. 4 0
      src/cowboy_rest.erl
  2. 23 0
      test/handlers/content_types_accepted_h.erl
  3. 34 20
      test/rest_handler_SUITE.erl

+ 4 - 0
src/cowboy_rest.erl

@@ -1060,6 +1060,10 @@ accept_resource(Req, State) ->
 		{CTA, Req2, State2} ->
 			CTA2 = [normalize_content_types(P) || P <- CTA],
 			try cowboy_req:parse_header(<<"content-type">>, Req2) of
+				%% We do not match against the boundary parameter for multipart.
+				{Type = <<"multipart">>, SubType, Params} ->
+					ContentType = {Type, SubType, lists:keydelete(<<"boundary">>, 1, Params)},
+					choose_content_type(Req2, State2, ContentType, CTA2);
 				ContentType ->
 					choose_content_type(Req2, State2, ContentType, CTA2)
 			catch _:_ ->

+ 23 - 0
test/handlers/content_types_accepted_h.erl

@@ -0,0 +1,23 @@
+%% This module accepts a multipart media type with parameters
+%% that do not include boundary.
+
+-module(content_types_accepted_h).
+
+-export([init/2]).
+-export([allowed_methods/2]).
+-export([content_types_accepted/2]).
+-export([put_multipart_mixed/2]).
+
+init(Req, Opts) ->
+	{cowboy_rest, Req, Opts}.
+
+allowed_methods(Req, State) ->
+	{[<<"PUT">>], Req, State}.
+
+content_types_accepted(Req=#{qs := <<"multipart">>}, State) ->
+	{[
+		{{<<"multipart">>, <<"mixed">>, [{<<"v">>, <<"1">>}]}, put_multipart_mixed}
+	], Req, State}.
+
+put_multipart_mixed(Req, State) ->
+	{true, Req, State}.

+ 34 - 20
test/rest_handler_SUITE.erl

@@ -47,6 +47,7 @@ init_dispatch(_) ->
 			charset_in_content_types_provided_implicit_h, []},
 		{"/charset_in_content_types_provided_implicit_no_callback",
 			charset_in_content_types_provided_implicit_no_callback_h, []},
+		{"/content_types_accepted", content_types_accepted_h, []},
 		{"/if_range", if_range_h, []},
 		{"/provide_callback_missing", provide_callback_missing_h, []},
 		{"/provide_range_callback", provide_range_callback_h, []},
@@ -69,26 +70,6 @@ do_decode(Headers, Body) ->
 
 %% Tests.
 
-error_on_malformed_if_match(Config) ->
-	doc("A malformed If-Match header must result in a 400 response."),
-	ConnPid = gun_open(Config),
-	Ref = gun:get(ConnPid, "/", [
-		{<<"accept-encoding">>, <<"gzip">>},
-		{<<"if-match">>, <<"bad">>}
-	]),
-	{response, _, 400, _} = gun:await(ConnPid, Ref),
-	ok.
-
-error_on_malformed_if_none_match(Config) ->
-	doc("A malformed If-None-Match header must result in a 400 response."),
-	ConnPid = gun_open(Config),
-	Ref = gun:get(ConnPid, "/", [
-		{<<"accept-encoding">>, <<"gzip">>},
-		{<<"if-none-match">>, <<"bad">>}
-	]),
-	{response, _, 400, _} = gun:await(ConnPid, Ref),
-	ok.
-
 charset_in_content_types_provided(Config) ->
 	doc("When a charset is matched explictly in content_types_provided, "
 		"that charset is used and the charsets_provided callback is ignored."),
@@ -287,6 +268,39 @@ charsets_provided_empty_noheader(Config) ->
 	{response, _, 406, _} = gun:await(ConnPid, Ref),
 	ok.
 
+content_types_accepted_ignore_multipart_boundary(Config) ->
+	doc("When a multipart content-type is provided for the request "
+		"body, the boundary parameter is not expected to be returned "
+		"from the content_types_accepted callback and will be "
+		"automatically ignored."),
+	ConnPid = gun_open(Config),
+	Ref = gun:put(ConnPid, "/content_types_accepted?multipart", [
+		{<<"accept-encoding">>, <<"gzip">>},
+		{<<"content-type">>, <<"multipart/mixed; boundary=abcdef; v=1">>}
+	], <<"Not really multipart!">>),
+	{response, _, 204, _} = gun:await(ConnPid, Ref),
+	ok.
+
+error_on_malformed_if_match(Config) ->
+	doc("A malformed If-Match header must result in a 400 response."),
+	ConnPid = gun_open(Config),
+	Ref = gun:get(ConnPid, "/", [
+		{<<"accept-encoding">>, <<"gzip">>},
+		{<<"if-match">>, <<"bad">>}
+	]),
+	{response, _, 400, _} = gun:await(ConnPid, Ref),
+	ok.
+
+error_on_malformed_if_none_match(Config) ->
+	doc("A malformed If-None-Match header must result in a 400 response."),
+	ConnPid = gun_open(Config),
+	Ref = gun:get(ConnPid, "/", [
+		{<<"accept-encoding">>, <<"gzip">>},
+		{<<"if-none-match">>, <<"bad">>}
+	]),
+	{response, _, 400, _} = gun:await(ConnPid, Ref),
+	ok.
+
 if_range_etag_equal(Config) ->
 	doc("When the if-range header matches, a 206 partial content "
 		"response is expected for an otherwise valid range request. (RFC7233 3.2)"),