From 1f774ab2168ae13239fba6aab185cca9e5d7e26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Thu, 2 Aug 2018 17:05:37 +0200 Subject: Fix parsing of lone id: lines in cow_sse --- src/cow_sse.erl | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/src/cow_sse.erl b/src/cow_sse.erl index 9d4a91e..7aa98ce 100644 --- a/src/cow_sse.erl +++ b/src/cow_sse.erl @@ -23,6 +23,7 @@ state_name = bom :: bom | events, buffer = <<>> :: binary(), last_event_id = <<>> :: binary(), + last_event_id_set = false :: boolean(), event_type = <<>> :: binary(), data = [] :: iolist(), retry = undefined :: undefined | non_neg_integer() @@ -115,7 +116,7 @@ process_field(<<"event">>, Value, State) -> process_field(<<"data">>, Value, State=#state{data=Data}) -> {ok, State#state{data=[<<$\n>>, Value|Data]}}; process_field(<<"id">>, Value, State) -> - {ok, State#state{last_event_id=Value}}; + {ok, State#state{last_event_id=Value, last_event_id_set=true}}; process_field(<<"retry">>, Value, State) -> try {ok, State#state{retry=binary_to_integer(Value)}} @@ -126,8 +127,15 @@ process_field(_, _, State) -> {ok, State}. %% Data is an empty string; abort. -dispatch_event(State=#state{data=[]}) -> +dispatch_event(State=#state{last_event_id_set=false, data=[]}) -> {ok, State#state{event_type= <<>>}}; +%% Data is an empty string but we have a last_event_id: +%% propagate it on its own so that the caller knows the +%% most recent ID. +dispatch_event(State=#state{last_event_id=LastEventID, data=[]}) -> + {event, #{ + last_event_id => LastEventID + }, State#state{last_event_id_set=false, event_type= <<>>}}; %% Dispatch the event. %% %% Always remove the last linebreak from the data. @@ -140,10 +148,9 @@ dispatch_event(State=#state{last_event_id=LastEventID, _ -> EventType end, data => lists:reverse(Data) - }, State#state{event_type= <<>>, data=[]}}. + }, State#state{last_event_id_set=false, event_type= <<>>, data=[]}}. -ifdef(TEST). - parse_example1_test() -> {event, #{ event_type := <<"message">>, @@ -222,6 +229,47 @@ parse_example4_test() -> {more, _} = parse(<<>>, State), ok. +parse_id_without_data_test() -> + {event, Event1, State0} = parse(<< + "id: 1\n" + "\n" + "data: data\n" + "\n" + "id: 2\n" + "\n">>, init()), + 1 = maps:size(Event1), + #{last_event_id := <<"1">>} = Event1, + {event, #{ + event_type := <<"message">>, + last_event_id := <<"1">>, + data := Data + }, State1} = parse(<<>>, State0), + <<"data">> = iolist_to_binary(Data), + {event, Event2, State} = parse(<<>>, State1), + 1 = maps:size(Event2), + #{last_event_id := <<"2">>} = Event2, + {more, _} = parse(<<>>, State), + ok. + +parse_repeated_id_without_data_test() -> + {event, Event1, State0} = parse(<< + "id: 1\n" + "\n" + "event: message\n" %% This will be ignored since there's no data. + "\n" + "id: 1\n" + "\n" + "id: 2\n" + "\n">>, init()), + {event, Event1, State1} = parse(<<>>, State0), + 1 = maps:size(Event1), + #{last_event_id := <<"1">>} = Event1, + {event, Event2, State} = parse(<<>>, State1), + 1 = maps:size(Event2), + #{last_event_id := <<"2">>} = Event2, + {more, _} = parse(<<>>, State), + ok. + parse_split_event_test() -> {more, State} = parse(<< "data: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" @@ -229,7 +277,6 @@ parse_split_event_test() -> "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA">>, init()), {event, _, _} = parse(<<"==\n\n">>, State), ok. - -endif. -spec events([event()]) -> iolist(). -- cgit v1.2.3