anchor.diff:  handle_recv({error, Error}, _App, _State) ->
       {stop, {recv_error, Error}}.
   
  -is_eof(<<>>) -> true;
  -is_eof(_) -> false.
  -
  -error_on_recv_body_after_eof(false, _Result, _App) -> ok;
  -error_on_recv_body_after_eof(true, {recv_body, _, _, _}, App) ->
  -    error({recv_body_after_eof, App});
  -error_on_recv_body_after_eof(true, {recv_body, _, _, _, _}, App) ->
  -    error({recv_body_after_eof, App});
  -error_on_recv_body_after_eof(true, _Result, _App) -> ok.
  -
  -increment_recv_len(I, #state{recv_len=Len}=S) ->
  -    S#state{recv_len=I + Len}.
  +%%%===================================================================
  +%%% recv_form_data support
  +%%%===================================================================
   
   handle_app_recv_form_data(App, Env, Options, State) ->
       ContentType = psycho:env_val(content_type, Env),
  -    handle_recv_form_data_type(ContentType, App, setenv(Env, State), Options).
  -
  -handle_recv_form_data_type("application/x-www-form-urlencoded",
  -                           App, State, Options) ->
  +    handle_app_recv_form_data_type(
  +      ContentType, App, setenv(Env, State), Options).
  +
  +handle_app_recv_form_data_type(
  +  "application/x-www-form-urlencoded", App, State, Options) ->
  +    handle_app_recv_urlencoded_data(App, State, Options);
  +handle_app_recv_form_data_type(
  +  "multipart/form-data;"++_, App, State, Options) ->
  +    handle_app_recv_multipart(App, State, Options);
  +handle_app_recv_form_data_type(ContentType, _App, State, _Options) ->
  +    respond(internal_error("Unsupported content type"), State),
  +    error({form_data_content_type, ContentType}).
  +
  +%%%===================================================================
  +%%% urlencoded form data
  +%%%===================================================================
  +
  +handle_app_recv_urlencoded_data(App, State, Options) ->
       Timeout = proplists:get_value(recv_timeout, Options, ?IDLE_TIMEOUT),
       RecvResult = recv_remaining(Timeout, State),
  -    handle_recv_urlencoded_form_data(RecvResult, App, State);
  -handle_recv_form_data_type(ContentType, _App, State, _Options) ->
  -    respond(internal_error("Unsupported content type"), State),
  -    error({form_data_content_type, ContentType}).
  -
  -internal_error() ->
  -    internal_error("Server error").
  -
  -internal_error(Msg) ->
  -    {?status_internal_server_error, [{"Content-Type", "text/plain"}], Msg}.
  +    handle_recv_urlencoded_form_data(RecvResult, App, State).
   
   handle_recv_urlencoded_form_data({ok, Data}, App, #state{env=Env}=State) ->
       DecodedData = decode_urlencoded_form_data(Data),
  
positive.code:       recv(safe_recv_len(Length, State), Timeout, State),
        App, setenv(Env, State)).
  
  setenv(Env, S) -> S#state{env=Env}.
  
  safe_recv_len(Requested, #state{req_content_len=Total, recv_len=Received}) ->
      min(Requested, Total - Received).
  
  recv(Length, Timeout, #state{sock=Sock}) when Length > 0 ->
      gen_tcp:recv(Sock, Length, Timeout);
  recv(_Length, _Timeout, _State) ->
      {ok, <<>>}.
  
  handle_recv({ok, Data}, App, #state{env=Env}=State) ->
      AppResult = (catch psycho:call_app_with_data(App, Env, Data)),
      error_on_recv_body_after_eof(is_eof(Data), AppResult, App),
  
negative.code:     respond(internal_error(), State),
      error({bad_return_value, Other}).
  
  %%%===================================================================
  %%% recv_body support
  %%%===================================================================
  
  handle_app_recv_body(App, Env, Options, State) ->
      Length = proplists:get_value(recv_length, Options, ?DEFAULT_RECV_LEN),
      Timeout = proplists:get_value(recv_timeout, Options, ?IDLE_TIMEOUT),
  