anchor.diff:     response,% active response
   
      iolen,   % expected length of data
  +   pckt,    % number of processed payload chunks
   
      opts,    % http protocol options
      buffer   % I/O buffer
  
positive.code:    'REQUESTED'/2,  %% server is requested
     'RESPONSE'/2,   %% server is responding
     'STREAM'/2,     %% payload is streamed (chunked)
     'CHUNK'/2,      %% receive chunk
     'RECV'/2        %% payload is received
  ]). 
  
  
negative.code: %%%
  %%%------------------------------------------------------------------ 
  'REQUESTED'({_Prot, _Peer, {recv, Chunk}}, #fsm{buffer=Buf}=S) ->
     parse_status_line(S#fsm{buffer = <<Buf/binary, Chunk/binary>>}).
  
  %%%------------------------------------------------------------------
  %%%
  %%% RESPONSE: status line is received, waiting for headers
  %%%
  %%%------------------------------------------------------------------ 
  'RESPONSE'({_Prot, _Peer, {recv, Chunk}}, #fsm{buffer=Buf}=S) ->
     parse_header(S#fsm{buffer = <<Buf/binary, Chunk/binary>>}).
  
  %%%------------------------------------------------------------------
  %%%
  %%% RECV: receive response payload
  %%%
  %%%------------------------------------------------------------------ 
  'RECV'({_Prot, _Peer, {recv, Data}}, #fsm{buffer=Buf}=S) ->
     parse_payload(S#fsm{buffer = <<Buf/binary, Data/binary>>}).
  
  
  %%%------------------------------------------------------------------
  %%%
  %%% STREAM
  %%%
  %%%------------------------------------------------------------------ 
  'STREAM'({_Prot, _Peer, {recv, Data}}, #fsm{buffer=Buf}=S) ->
     parse_chunk(S#fsm{buffer = <<Buf/binary, Data/binary>>});
  
  'STREAM'(timeout, S) ->
     parse_chunk(S).
  
  %%%------------------------------------------------------------------
  %%%
  %%% http response parser 
  %%%
  %%%------------------------------------------------------------------   
  
  %%
  %%
  parse_status_line(#fsm{buffer=Buffer}=S) ->   
     % TODO: error handling policy
     % TODO: {ok, {http_error, ...}}
     case erlang:decode_packet(http_bin, Buffer, []) of
        {more, _}       -> {next_state, 'REQUESTED', S, ?T_SERVER};
        {error, Reason} -> {error, Reason};
        {ok, Req,Chunk} -> parse_status_line(Req, S#fsm{buffer=Chunk})
     end.
  
  parse_status_line({http_response, _Vsn, Code, Msg}, 
                    #fsm{request={{Method, _}, Uri}}=S) ->
     lager:debug("http ~p ~p ~p ~p", [Method, Uri, Code, Msg]), 
     parse_header(S#fsm{response={Code, []}});
  
  parse_status_line({http_error, Msg}, S) ->
     {error, Msg}.
  
  %%
  %%
  parse_header(#fsm{buffer=Buffer}=S) -> 
     % TODO: error handling policy
     % TODO: {ok, {http_error, ...}}  
     case erlang:decode_packet(httph_bin, Buffer, []) of
        {more, _}       -> {next_state, 'RESPONSE', S};
        {error, Reason} -> {error, Reason};
        {ok, Req,Chunk} -> parse_header(Req, S#fsm{buffer=Chunk})
     end.
  
  parse_header({http_header, _I, 'Content-Length'=Head, _R, Val}, 
               #fsm{response={Code, Heads}}=S) ->
     Len = list_to_integer(binary_to_list(Val)),
     parse_header(S#fsm{response={Code, [{Head, Len} | Heads]}, iolen=Len});
  
  parse_header({http_header, _I, Head, _R, Val},
             #fsm{response={Code, Heads}}=S) ->
     parse_header(S#fsm{response={Code, [{Head, Val} | Heads]}});
  
  parse_header(http_eoh, #fsm{request={_, Uri}, response=Rsp, iolen=0}=S) ->
     % expected length of response is not known, stream it
     parse_chunk(S#fsm{pckt=0});
  
  parse_header(http_eoh, #fsm{request={_, Uri}, response=Rsp}=S) ->
     % exprected length of response is know, receive it
     parse_payload(S#fsm{pckt=0}).
  
  %%
  %%
  parse_payload(#fsm{request={_, Uri}, response=Rsp, pckt=Pckt, iolen=Len, buffer=Buffer}=S) ->
     case size(Buffer) of
        % buffer equals or exceed expected payload size
        % end of data stream is reached.
        Size when Size >= Len ->
           <<Chunk:Len/binary, _/binary>> = Buffer,
           Msg = if
              Pckt =:= 0 -> [{http, Uri, Rsp}, {http, Uri, {recv, Chunk}}, {http, Uri, eof}];
              true       -> [{http, Uri, {recv, Chunk}}, {http, Uri, eof}]
           end,
           {emit, Msg, 'CONNECTED', 
              S#fsm{
                 pckt  = Pckt + 1,
                 buffer= <<>>
              }
           };
        Size when Size < Len ->
           Msg = if
              Pckt =:= 0 -> [{http, Uri, Rsp}, {http, Uri, {recv, Buffer}}];
              true       -> {http, Uri, {recv, Buffer}}
           end,
           {emit, Msg, 'RECV',
              S#fsm{
                 pckt  = Pckt + 1,
                 iolen = Len - size(Buffer), 
                 buffer= <<>>
              }
           }
     end.
  
  %%
  %%
  parse_chunk(#fsm{request={_, Uri}, response=Rsp, pckt=Pckt, iolen=0, buffer=Buffer}=S) ->
     case binary:split(Buffer, <<"\r\n">>) of  
        [_]          -> 
           {next_state, 'STREAM', S};
        [Head, Data] -> 
           [L |_] = binary:split(Head, [<<" ">>, <<";">>]),
           Len    = list_to_integer(binary_to_list(L), 16),
           if
              % chunk with length 0 is last chunk is stream
              Len =:= 0 ->
                 Msg = if
                    Pckt =:= 0 -> [{http, Uri, Rsp}, {http, Uri, eof}];
                    true       -> {http, Uri, eof}
                 end,
                 {emit, Msg, 'CONNECTED', S};
              true      ->
                 parse_chunk(S#fsm{iolen=Len, buffer=Data})
           end
     end;
  
  parse_chunk(#fsm{request={_, Uri}, response=Rsp, pckt=Pckt, iolen=Len, buffer=Buffer}=S) ->
     case size(Buffer) of
        Size when Size >= Len ->
           <<Chunk:Len/binary, $\r, $\n, Rest/binary>> = Buffer,
           Msg = if
              Pckt =:= 0 -> [{http, Uri, Rsp}, {http, Uri, {recv, Chunk}}];
              true       -> {http, Uri, {recv, Chunk}}
           end,
           {emit, Msg, 'STREAM',
              S#fsm{
                 iolen = 0, 
                 buffer= Rest,
                 pckt  = Pckt + 1
              },
              0    %re-sched via timeout
           };
        Size when Size < Len ->
           Msg = if
              Pckt =:= 0 -> [{http, Uri, Rsp}, {http, Uri, {recv, Buffer}}];
              true       -> {http, Uri, {recv, Buffer}}
           end,
           {emit, Msg, 'STREAM', 
              S#fsm{
                 iolen = Len - size(Buffer), 
                 buffer= <<>>,
                 pckt  = Pckt + 1
              }
           }
     end.
  
  
  %%%------------------------------------------------------------------
  %%%
  