From 7728c624028db160edad2646f34d069d16af0869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Wed, 15 Nov 2017 14:32:51 +0100 Subject: Add support for chunked transfer-encoding trailers It considers all 0-sized chunks that aren't \r\n\r\n to be trailers. There's no option for enabling/disabling the behavior (for example when the te header was sent). It doesn't parse the trailer, it's up to the user to parse it separately via the new cow_http:headers/1 functions. Note that this reuses the TotalLength part of the returned 'done' tuple to signal whether there are trailers. This value has been ignored in Cowboy since 2.0 and was just a historical leftover. I'm not aware of anyone using this module outside of Gun or Cowboy, so I don't expect this to break anything. If it does, well, it's not a documented function anyway. Your fault. --- src/cow_http_te.erl | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) (limited to 'src/cow_http_te.erl') diff --git a/src/cow_http_te.erl b/src/cow_http_te.erl index e0e413d..b6290b7 100644 --- a/src/cow_http_te.erl +++ b/src/cow_http_te.erl @@ -199,8 +199,15 @@ chunked_len(<< $f, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 15); %% chunk extensions (unlikely) we will need to change this clause too. chunked_len(<< C, R/bits >>, S, A, Len) when C =/= $\r -> skip_chunk_ext(R, S, A, Len); %% Final chunk. -chunked_len(<< "\r\n\r\n", R/bits >>, S, <<>>, 0) -> {done, S, R}; -chunked_len(<< "\r\n\r\n", R/bits >>, S, A, 0) -> {done, A, S, R}; +%% +%% When trailers are following we simply return them as the Rest. +%% Then the user code can decide to call the stream_trailers function +%% to parse them. The user can therefore ignore trailers as necessary +%% if they do not wish to handle them. +chunked_len(<< "\r\n\r\n", R/bits >>, _, <<>>, 0) -> {done, no_trailers, R}; +chunked_len(<< "\r\n\r\n", R/bits >>, _, A, 0) -> {done, A, no_trailers, R}; +chunked_len(<< "\r\n", R/bits >>, _, <<>>, 0) when byte_size(R) > 2 -> {done, trailers, R}; +chunked_len(<< "\r\n", R/bits >>, _, A, 0) when byte_size(R) > 2 -> {done, A, trailers, R}; chunked_len(_, _, _, 0) -> more; %% Normal chunk. Add 2 to Len for the trailing \r\n. chunked_len(<< "\r\n", R/bits >>, S, A, Len) -> {next, R, {Len + 2, S}, A}; @@ -229,7 +236,7 @@ last_chunk() -> -ifdef(TEST). stream_chunked_identity_test() -> - {done, <<"Wikipedia in\r\n\r\nchunks.">>, 23, <<>>} + {done, <<"Wikipedia in\r\n\r\nchunks.">>, no_trailers, <<>>} = stream_chunked(iolist_to_binary([ chunk("Wiki"), chunk("pedia"), @@ -239,8 +246,8 @@ stream_chunked_identity_test() -> ok. stream_chunked_one_pass_test() -> - {done, 0, <<>>} = stream_chunked(<<"0\r\n\r\n">>, {0, 0}), - {done, <<"Wikipedia in\r\n\r\nchunks.">>, 23, <<>>} + {done, no_trailers, <<>>} = stream_chunked(<<"0\r\n\r\n">>, {0, 0}), + {done, <<"Wikipedia in\r\n\r\nchunks.">>, no_trailers, <<>>} = stream_chunked(<< "4\r\n" "Wiki\r\n" @@ -251,7 +258,7 @@ stream_chunked_one_pass_test() -> "0\r\n" "\r\n">>, {0, 0}), %% Same but with extra spaces or chunk extensions. - {done, <<"Wikipedia in\r\n\r\nchunks.">>, 23, <<>>} + {done, <<"Wikipedia in\r\n\r\nchunks.">>, no_trailers, <<>>} = stream_chunked(<< "4 \r\n" "Wiki\r\n" @@ -261,6 +268,19 @@ stream_chunked_one_pass_test() -> " in\r\n\r\nchunks.\r\n" "0;ext\r\n" "\r\n">>, {0, 0}), + %% Same but with trailers. + {done, <<"Wikipedia in\r\n\r\nchunks.">>, trailers, Rest} + = stream_chunked(<< + "4\r\n" + "Wiki\r\n" + "5\r\n" + "pedia\r\n" + "e\r\n" + " in\r\n\r\nchunks.\r\n" + "0\r\n" + "x-foo-bar: bar foo\r\n" + "\r\n">>, {0, 0}), + {[{<<"x-foo-bar">>, <<"bar foo">>}], <<>>} = cow_http:parse_headers(Rest), ok. stream_chunked_n_passes_test() -> @@ -270,7 +290,7 @@ stream_chunked_n_passes_test() -> {more, <<"Wiki">>, 0, S2} = stream_chunked(<<"Wiki\r\n">>, S1), {more, <<"pedia">>, <<"e\r">>, S3} = stream_chunked(<<"5\r\npedia\r\ne\r">>, S2), {more, <<" in\r\n\r\nchunks.">>, 2, S4} = stream_chunked(<<"e\r\n in\r\n\r\nchunks.">>, S3), - {done, 23, <<>>} = stream_chunked(<<"\r\n0\r\n\r\n">>, S4), + {done, no_trailers, <<>>} = stream_chunked(<<"\r\n0\r\n\r\n">>, S4), %% A few extra for coverage purposes. more = stream_chunked(<<"\n3">>, {1, 0}), {more, <<"abc">>, 2, {2, 3}} = stream_chunked(<<"\n3\r\nabc">>, {1, 0}), -- cgit v1.2.3