Skip to content
Permalink
Browse files
add ability to handle combined content-length header.
  • Loading branch information
kmwang committed Oct 9, 2012
1 parent 9d205a8 commit 888ceb58d6c6cc24d5c6ad375249b5f636570bbf
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 6 deletions.
@@ -6,7 +6,7 @@
-module(mochiweb_headers).
-author('bob@mochimedia.com').
-export([empty/0, from_list/1, insert/3, enter/3, get_value/2, lookup/2]).
-export([delete_any/2, get_primary_value/2]).
-export([delete_any/2, get_primary_value/2, get_combined_value/2]).
-export([default/3, enter_from_list/2, default_from_list/2]).
-export([to_list/1, make/1]).
-export([from_binary/1]).
@@ -112,6 +112,32 @@ get_primary_value(K, T) ->
lists:takewhile(fun (C) -> C =/= $; end, V)
end.

%% @spec get_combined_value(key(), headers()) -> string() | undefined
%% @doc Return the value from the given header using a case insensitive search.
%% If the value of the header is a comma-separated list where holds values
%% are all identical, the identical value will be returned.
%% undefined will be returned for keys that are not present or the
%% values in the list are not the same.
%%
%% Section 4.2 of the RFC 2616 (HTTP 1.1) describes multiple message-header
%% fields with the same field-name may be present in a message if and only
%% if the entire field-value for that header field is defined as a
%% comma-separated list [i.e., #(values)].
get_combined_value(K, T) ->
case get_value(K, T) of
undefined ->
undefined;
V ->
UniqueValuesList = sets:to_list(sets:from_list(string:tokens(V, " ,"))),
case length(UniqueValuesList) =:= 1 of
true ->
[Val|_Ignore] = UniqueValuesList,
Val;
false ->
undefined
end
end.

%% @spec lookup(key(), headers()) -> {value, {key(), string()}} | none
%% @doc Return the case preserved key and value for the given header using
%% a case insensitive search. none will be returned for keys that are
@@ -237,6 +263,37 @@ get_primary_value_test() ->
get_primary_value(<<"baz">>, H)),
ok.

get_combined_value_test() ->
H = make([{hdr, foo}, {baz, <<"wibble,taco">>}, {content_length, "123, 123"},
{test, " 123, 123, 123 , 123,123 "},
{test2, "456, 123, 123 , 123"},
{test3, "123"}, {test4, " 123, "}]),
?assertEqual(
"foo",
get_combined_value(hdr, H)),
?assertEqual(
undefined,
get_combined_value(bar, H)),
?assertEqual(
undefined,
get_combined_value(<<"baz">>, H)),
?assertEqual(
"123",
get_combined_value(<<"content_length">>, H)),
?assertEqual(
"123",
get_combined_value(<<"test">>, H)),
?assertEqual(
undefined,
get_combined_value(<<"test2">>, H)),
?assertEqual(
"123",
get_combined_value(<<"test3">>, H)),
?assertEqual(
"123",
get_combined_value(<<"test4">>, H)),
ok.

set_cookie_test() ->
H = make([{"set-cookie", foo}, {"set-cookie", bar}, {"set-cookie", baz}]),
?assertEqual(
@@ -128,7 +128,7 @@ default_file_handler_1(Filename, ContentType, Acc) ->

parse_multipart_request(Req, Callback) ->
%% TODO: Support chunked?
Length = list_to_integer(Req:get_header_value("content-length")),
Length = list_to_integer(Req:get_combined_header_value("content-length")),
Boundary = iolist_to_binary(
get_boundary(Req:get_header_value("content-type"))),
Prefix = <<"\r\n--", Boundary/binary>>,
@@ -11,7 +11,7 @@

-define(QUIP, "Any of you quaids got a smint?").

-export([get_header_value/1, get_primary_header_value/1, get/1, dump/0]).
-export([get_header_value/1, get_primary_header_value/1, get_combined_header_value/1, get/1, dump/0]).
-export([send/1, recv/1, recv/2, recv_body/0, recv_body/1, stream_body/3]).
-export([start_response/1, start_response_length/1, start_raw_response/1]).
-export([respond/1, ok/1]).
@@ -52,6 +52,9 @@ get_header_value(K) ->
get_primary_header_value(K) ->
mochiweb_headers:get_primary_value(K, Headers).

get_combined_header_value(K) ->
mochiweb_headers:get_combined_value(K, Headers).

%% @type field() = socket | scheme | method | raw_path | version | headers | peer | path | body_length | range

%% @spec get(field()) -> term()
@@ -167,7 +170,7 @@ recv(Length, Timeout) ->
body_length() ->
case get_header_value("transfer-encoding") of
undefined ->
case get_header_value("content-length") of
case get_combined_header_value("content-length") of
undefined ->
undefined;
Length ->
@@ -389,8 +392,8 @@ should_close() ->
andalso get_header_value("connection") =/= "Keep-Alive")
%% unread data left on the socket, can't safely continue
orelse (DidNotRecv
andalso get_header_value("content-length") =/= undefined
andalso list_to_integer(get_header_value("content-length")) > 0)
andalso get_combined_header_value("content-length") =/= undefined
andalso list_to_integer(get_combined_header_value("content-length")) > 0)
orelse (DidNotRecv
andalso get_header_value("transfer-encoding") =:= "chunked").

0 comments on commit 888ceb5

Please sign in to comment.