Browse files

Parse extensions and trailing headers of chunked transfer encoded req…

…uests

Chunk extensions and trailing headers are parsed but ignored in requests
and are not supported in responses. It is a strange feature of the RFC-2619
and would be hard to implement. Moreover, this feature is rarely supported
by clients and servers and even less used.
So, without any concrete use-case, no further work will be done on this
subject.
  • Loading branch information...
1 parent f3a31fe commit a66f8a3cb1789265431b2501308e1a75ffcc53dc @capflam capflam committed Sep 12, 2012
Showing with 69 additions and 27 deletions.
  1. +24 −14 src/yaws.erl
  2. +5 −2 src/yaws_dav.erl
  3. +12 −8 src/yaws_revproxy.erl
  4. +4 −3 src/yaws_server.erl
  5. +24 −0 test/t2/app_test.erl
View
38 src/yaws.erl
@@ -30,8 +30,9 @@
-export([first/2, elog/2, filesize/1, upto/2, to_string/1, to_list/1,
integer_to_hex/1, hex_to_integer/1, string_to_hex/1, hex_to_string/1,
is_modified_p/2, flag/3, dohup/1, is_ssl/1, address/0, is_space/1,
- setopts/3, eat_crnl/2, get_chunk_num/2, get_chunk/4, list_to_uue/1,
- uue_to_list/1, printversion/0, strip_spaces/1, strip_spaces/2,
+ setopts/3, eat_crnl/2, get_chunk_num/2, get_chunk_header/2,
+ get_chunk/4, get_chunk_trailer/2, list_to_uue/1, uue_to_list/1,
+ printversion/0, strip_spaces/1, strip_spaces/2,
month/1, mk2/1, home/0, arg_rewrite/1, to_lowerchar/1, to_lower/1,
funreverse/2, is_prefix/2, split_sep/2, join_sep/2, accepts_gzip/2,
upto_char/2, deepmap/2, ticker/2, ticker/3,
@@ -2176,26 +2177,27 @@ eat_crnl(Fd,SSL) ->
case do_recv(Fd,0, SSL) of
{ok, <<13,10>>} -> ok;
{ok, [13,10]} -> ok;
- Err -> {error, Err}
+ _ -> exit(normal)
end.
-get_chunk_num(Fd,SSL) ->
+
+get_chunk_num(Fd, SSL) ->
+ {N, _} = get_chunk_header(Fd, SSL),
+ N.
+
+get_chunk_header(Fd, SSL) ->
case do_recv(Fd, 0, SSL) of
- {ok, Line} ->
+ {ok, Data} ->
+ Line = if is_binary(Data) -> binary_to_list(Data);
+ true -> Data
+ end,
?Debug("Get chunk num from line ~p~n",[Line]),
- erlang:list_to_integer(nonl(Line),16);
+ {N, Exts} = split_at(Line, $;),
+ {list_to_integer(strip_spaces(N),16), strip_spaces(Exts)};
{error, _Rsn} ->
exit(normal)
end.
-nonl(B) when is_binary(B) -> nonl(binary_to_list(B));
-nonl([10|T]) -> nonl(T);
-nonl([13|T]) -> nonl(T);
-nonl([32|T]) -> nonl(T);
-nonl([H|T]) -> [H|nonl(T)];
-nonl([]) -> [].
-
-
get_chunk(_Fd, N, N, _) ->
[];
@@ -2208,6 +2210,14 @@ get_chunk(Fd, N, Asz,SSL) ->
exit(normal)
end.
+get_chunk_trailer(Fd, SSL) ->
+ Hdrs = #headers{},
+ case http_collect_headers(Fd, undefined, Hdrs, SSL, 0) of
+ {error,_} -> exit(normal);
+ Hdrs -> <<>>;
+ NewHdrs -> {<<>>, NewHdrs}
+ end.
+
%% split inputstring at first occurrence of Char
split_at(String, Char) ->
split_at(String, Char, []).
View
7 src/yaws_dav.erl
@@ -509,11 +509,14 @@ store_client_data(Fd, CliSock, Len, SSlBool) ->
%% not nice to support this for ssl sockets
store_chunked_client_data(Fd, CliSock, SSL) ->
yaws:setopts(CliSock, [binary, {packet, line}], SSL),
- N = yaws:get_chunk_num(CliSock, SSL),
+ %% Ignore chunk extentions
+ {N, _Exts} = yaws:get_chunk_header(CliSock, SSL),
yaws:setopts(CliSock, [binary, {packet, raw}], SSL),
if
N == 0 ->
- _Tmp=yaws:do_recv(CliSock, 2, SSL);%% flush last crnl
+ %% Ignore chunk trailer
+ yaws:get_chunk_trailer(CliSock, SSL),
+ ok;
true ->
B = yaws:get_chunk(CliSock, N, 0,SSL),
yaws:eat_crnl(CliSock,SSL),
View
20 src/yaws_revproxy.erl
@@ -625,15 +625,19 @@ read(RPState = #revproxy{srvsock=Sock, type=Type}, Len, Data) ->
read_chunk(#revproxy{srvsock=Sock, type=Type}) ->
try
yaws:setopts(Sock, [binary, {packet, line}], Type),
- Len = yaws:get_chunk_num(Sock, Type),
+ %% Ignore chunk extentions
+ {Len, _Exts} = yaws:get_chunk_header(Sock, Type),
yaws:setopts(Sock, [binary, {packet, raw}], Type),
-
- Data = if
- Len == 0 -> <<>>;
- true -> yaws:get_chunk(Sock, Len, 0, Type)
- end,
- ok = yaws:eat_crnl(Sock, Type),
- {ok, iolist_to_binary(Data)}
+ if
+ Len == 0 ->
+ %% Ignore chunk trailer
+ yaws:get_chunk_trailer(Sock, Type),
+ {ok, <<>>};
+ true ->
+ B = yaws:get_chunk(Sock, Len, 0, Type),
+ ok = yaws:eat_crnl(Sock, Type),
+ {ok, iolist_to_binary(B)}
+ end
catch
_:Reason ->
{error, Reason}
View
7 src/yaws_server.erl
@@ -1755,7 +1755,6 @@ handle_normal_request(CliSock, ARG, UT, Authdirs, N) ->
{true, User} -> {true, set_auth_user(ARG, User)};
E -> {E, ARG}
end,
-
case IsAuth of
true ->
%%!todo - remove special treatment of appmod here. (after suitable
@@ -2592,7 +2591,8 @@ get_chunked_client_data(CliSock,SSL) ->
undefined;
Val =:= undefined ->
yaws:setopts(CliSock, [binary, {packet, line}],SSL),
- N = yaws:get_chunk_num(CliSock,SSL),
+ %% Ignore chunk extentions
+ {N, _Exts} = yaws:get_chunk_header(CliSock, SSL),
yaws:setopts(CliSock, [binary, {packet, raw}],SSL),
N;
true ->
@@ -2605,7 +2605,8 @@ get_chunked_client_data(CliSock,SSL) ->
<<>>;
Len == 0 ->
put(current_chunk_size, 0),
- _Tmp=yaws:do_recv(CliSock, 2, SSL),%% flush last crnl
+ %% Ignore chunk trailer
+ yaws:get_chunk_trailer(CliSock, SSL),
<<>>;
Len =< SC#sconf.partial_post_size ->
B = yaws:get_chunk(CliSock, Len, 0, SSL),
View
24 test/t2/app_test.erl
@@ -23,6 +23,7 @@ start() ->
test_json(),
test_post(),
test_flush(),
+ test_te_trailer_and_extensions(),
test_expires(),
test_reentrant(),
test_cgi_redirect(),
@@ -666,6 +667,29 @@ flush_chunked_get() ->
ibrowse:stop_worker_process(ConnPid),
ok.
+test_te_trailer_and_extensions() ->
+ io:format("te_trailer_and_extensions_test\n",[]),
+ {ok, Sock} = gen_tcp:connect("localhost", 8006, [binary, {active, false}]),
+ Data = ["This is the data in the first chunk\n",
+ "and this is the second one\n",
+ "con", "sequence"],
+ Path = "/posttest/chunked/" ++ integer_to_list(length(lists:flatten(Data))),
+ ?line gen_tcp:send(Sock, "POST "++Path++" HTTP/1.1\r\n"
+ "Host: localhost\r\n"
+ "Trailer: Content-Type\r\n"
+ "Trailer: Extra-Headers-WooHoo\r\n"
+ "Transfer-Encoding: Chunked\r\n\r\n"),
+ Body = lists:flatten([[integer_to_list(length(X), 16),"; foo=bar\r\n",
+ X,"\r\n"] || X <- Data]),
+ ?line gen_tcp:send(Sock, Body),
+ ?line gen_tcp:send(Sock, "0\r\n"
+ "Extra-Headers-WooHoo: something\r\n"
+ "Content-Type: text/plain\r\n\r\n"),
+ inet:setopts(Sock, [{packet, http}]),
+ ?line {ok, Len} = recv_hdrs(Sock),
+ gen_tcp:close(Sock),
+ ok.
+
test_expires() ->
io:format("expires_test\n", []),

0 comments on commit a66f8a3

Please sign in to comment.