Browse Source

Add cow_http_hd:parse_etag/1

From RFC7232.
Loïc Hoguin 10 years ago
parent
commit
d357eacb24
1 changed files with 61 additions and 15 deletions
  1. 61 15
      src/cow_http_hd.erl

+ 61 - 15
src/cow_http_hd.erl

@@ -22,6 +22,7 @@
 -export([parse_content_length/1]).
 -export([parse_content_type/1]).
 -export([parse_date/1]).
+-export([parse_etag/1]).
 -export([parse_expect/1]).
 -export([parse_if_match/1]).
 -export([parse_if_modified_since/1]).
@@ -965,6 +966,66 @@ parse_date_test_() ->
 	[{V, fun() -> R = parse_date(V) end} || {V, R} <- Tests].
 -endif.
 
+%% @doc Parse the ETag header.
+
+-spec parse_etag(binary()) -> etag().
+parse_etag(<< $W, $/, $", R/bits >>) ->
+	etag(R, weak, <<>>);
+parse_etag(<< $", R/bits >>) ->
+	etag(R, strong, <<>>).
+
+etag(<< $", R/bits >>, Strength, Tag) ->
+	ws_end(R),
+	{Strength, Tag};
+etag(<< C, R/bits >>, Strength, Tag) when ?IS_ETAGC(C) ->
+	etag(R, Strength, << Tag/binary, C >>).
+
+-ifdef(TEST).
+etagc() ->
+	?SUCHTHAT(C, int(16#21, 16#ff), C =/= 16#22 andalso C =/= 16#7f).
+
+etag() ->
+	?LET({Strength, Tag},
+		{oneof([weak, strong]), list(etagc())},
+		begin
+			TagBin = list_to_binary(Tag),
+			{{Strength, TagBin},
+				case Strength of
+					weak -> << $W, $/, $", TagBin/binary, $" >>;
+					strong -> << $", TagBin/binary, $" >>
+				end}
+		end).
+
+prop_parse_etag() ->
+	?FORALL({Tag, TagBin},
+		etag(),
+		Tag =:= parse_etag(TagBin)).
+
+parse_etag_test_() ->
+	Tests = [
+		{<<"\"xyzzy\"">>, {strong, <<"xyzzy">>}},
+		{<<"W/\"xyzzy\"">>, {weak, <<"xyzzy">>}},
+		{<<"\"\"">>, {strong, <<>>}}
+	],
+	[{V, fun() -> R = parse_etag(V) end} || {V, R} <- Tests].
+
+parse_etag_error_test_() ->
+	Tests = [
+		<<>>,
+		<<"\"">>,
+		<<"W">>,
+		<<"W/">>
+	],
+	[{V, fun() -> {'EXIT', _} = (catch parse_etag(V)) end} || V <- Tests].
+-endif.
+
+-ifdef(PERF).
+horse_parse_etag() ->
+	horse:repeat(200000,
+		parse_etag(<<"W/\"xyzzy\"">>)
+	).
+-endif.
+
 %% @doc Parse the Expect header.
 
 -spec parse_expect(binary()) -> continue.
@@ -1042,21 +1103,6 @@ etag_list_sep(<< $\t, R/bits >>, Acc) -> etag_list_sep(R, Acc);
 etag_list_sep(<< $,, R/bits >>, Acc) -> etag_list(R, Acc).
 
 -ifdef(TEST).
-etagc() ->
-	?SUCHTHAT(C, int(16#21, 16#ff), C =/= 16#22 andalso C =/= 16#7f).
-
-etag() ->
-	?LET({Strength, Tag},
-		{oneof([weak, strong]), list(etagc())},
-		begin
-			TagBin = list_to_binary(Tag),
-			{{Strength, TagBin},
-				case Strength of
-					weak -> << $W, $/, $", TagBin/binary, $" >>;
-					strong -> << $", TagBin/binary, $" >>
-				end}
-		end).
-
 prop_parse_if_match() ->
 	?FORALL(L,
 		non_empty(list(etag())),