From 9b8b042fb7d37b90994ef6b6615c92dd2769328e Mon Sep 17 00:00:00 2001 From: Chandrashekhar Mullaparthi Date: Tue, 7 Aug 2012 07:05:37 +0100 Subject: [PATCH] Fixed issue #67 with eyes wide open --- CHANGELOG | 3 ++ README.md | 2 +- src/ibrowse.app.src | 2 +- src/ibrowse_http_client.erl | 67 +++++++++++++++++------------------- test/ibrowse_test_server.erl | 2 +- 5 files changed, 37 insertions(+), 39 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index e635efc..7bfa5e0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ CONTRIBUTIONS & CHANGE HISTORY ============================== +07-08-2012 - v4.0.1 + * Fix issue 67 properly. + 03-08-2012 - v4.0.0 * Fixed a regression in handling HEAD. https://github.com/cmullaparthi/ibrowse/issues/67 diff --git a/README.md b/README.md index 8d0b764..2052ad7 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ibrowse is a HTTP client written in erlang. **Comments to:** chandrashekhar.mullaparthi@gmail.com -**Current Version:** 4.0.0 +**Current Version:** 4.0.1 **Latest Version:** git://github.com/cmullaparthi/ibrowse.git diff --git a/src/ibrowse.app.src b/src/ibrowse.app.src index 6a4aef8..facdfcd 100644 --- a/src/ibrowse.app.src +++ b/src/ibrowse.app.src @@ -1,6 +1,6 @@ {application, ibrowse, [{description, "Erlang HTTP client application"}, - {vsn, "4.0.0"}, + {vsn, "4.0.1"}, {modules, [ ibrowse, ibrowse_http_client, ibrowse_app, diff --git a/src/ibrowse_http_client.erl b/src/ibrowse_http_client.erl index d879b74..59b9e08 100644 --- a/src/ibrowse_http_client.erl +++ b/src/ibrowse_http_client.erl @@ -562,13 +562,13 @@ do_send_body(Body, State, _TE) -> do_send_body1(Source, Resp, State, TE) -> case Resp of - {ok, Data} when Data == []; Data == <<>> -> - do_send_body({Source}, State, TE); + {ok, Data} when Data == []; Data == <<>> -> + do_send_body({Source}, State, TE); {ok, Data} -> do_send(maybe_chunked_encode(Data, TE), State), do_send_body({Source}, State, TE); - {ok, Data, New_source_state} when Data == []; Data == <<>> -> - do_send_body({Source, New_source_state}, State, TE); + {ok, Data, New_source_state} when Data == []; Data == <<>> -> + do_send_body({Source, New_source_state}, State, TE); {ok, Data, New_source_state} -> do_send(maybe_chunked_encode(Data, TE), State), do_send_body({Source, New_source_state}, State, TE); @@ -994,8 +994,12 @@ chunk_request_body(Body, _ChunkSize, Acc) when is_list(Body) -> lists:reverse(["\r\n", LastChunk, Chunk | Acc]). -parse_response(_Data, #state{cur_req = undefined}=State) -> +parse_response(<<>>, #state{cur_req = undefined}=State) -> State#state{status = idle}; +parse_response(Data, #state{cur_req = undefined}=State) -> + do_trace("Data left to process when no pending request. ~1000.p~n", [Data]), + {error, data_in_status_idle}; + parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs, cur_req = CurReq} = State) -> #request{from=From, stream_to=StreamTo, req_id=ReqId, @@ -1013,58 +1017,49 @@ parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs, ConnClose = to_lower(get_value("connection", LCHeaders, "false")), IsClosing = is_connection_closing(HttpVsn, ConnClose), State_0 = case IsClosing of - true -> - shutting_down(State), - State#state{is_closing = IsClosing}; - false -> - State - end, + true -> + shutting_down(State), + State#state{is_closing = IsClosing}; + false -> + State + end, Give_raw_headers = get_value(give_raw_headers, Options, false), State_1 = case Give_raw_headers of true -> State_0#state{recvd_headers=Headers_1, status=get_body, - reply_buffer = <<>>, - status_line = Status_line, - raw_headers = Raw_headers, - http_status_code=StatCode}; + reply_buffer = <<>>, + status_line = Status_line, + raw_headers = Raw_headers, + http_status_code=StatCode}; false -> State_0#state{recvd_headers=Headers_1, status=get_body, - reply_buffer = <<>>, - http_status_code=StatCode} + reply_buffer = <<>>, + http_status_code=StatCode} end, put(conn_close, ConnClose), TransferEncoding = to_lower(get_value("transfer-encoding", LCHeaders, "false")), - Head_response_with_body = lists:member({workaround, head_response_with_body}, Options), + Head_response_with_body = lists:member({workaround, head_response_with_body}, Options), case get_value("content-length", LCHeaders, undefined) of _ when Method == connect, hd(StatCode) == $2 -> {_, Reqs_1} = queue:out(Reqs), cancel_timer(T_ref), upgrade_to_ssl(set_cur_request(State_0#state{reqs = Reqs_1, - recvd_headers = [], - status = idle - })); + recvd_headers = [], + status = idle + })); _ when Method == connect -> {_, Reqs_1} = queue:out(Reqs), do_error_reply(State#state{reqs = Reqs_1}, {error, proxy_tunnel_failed}), {error, proxy_tunnel_failed}; _ when Method =:= head, - Head_response_with_body =:= true -> - %% This is not supposed to happen, but it does. An - %% Apache server was observed to send an "empty" - %% body, but in a Chunked-Transfer-Encoding way, - %% which meant there was still a body. - %% Issue #67 on Github - {_, Reqs_1} = queue:out(Reqs), - send_async_headers(ReqId, StreamTo, Give_raw_headers, State_1), - State_1_1 = do_reply(State_1, From, StreamTo, ReqId, Resp_format, - {ok, StatCode, Headers_1, []}), - cancel_timer(T_ref, {eat_message, {req_timedout, From}}), - State_2 = reset_state(State_1_1), - State_3 = set_cur_request(State_2#state{reqs = Reqs_1}), - parse_response(Data_1, State_3); - _ when Method =:= head -> + Head_response_with_body =:= false -> + %% This (HEAD response with body) is not supposed + %% to happen, but it does. An Apache server was + %% observed to send an "empty" body, but in a + %% Chunked-Transfer-Encoding way, which meant + %% there was still a body. Issue #67 on Github {_, Reqs_1} = queue:out(Reqs), send_async_headers(ReqId, StreamTo, Give_raw_headers, State_1), State_1_1 = do_reply(State_1, From, StreamTo, ReqId, Resp_format, diff --git a/test/ibrowse_test_server.erl b/test/ibrowse_test_server.erl index 1cc0280..75d1b44 100644 --- a/test/ibrowse_test_server.erl +++ b/test/ibrowse_test_server.erl @@ -151,7 +151,7 @@ process_request(Sock, Sock_type, #request{method='HEAD', headers = _Headers, uri = {abs_path, "/ibrowse_head_transfer_enc"}}) -> - Resp = <<"HTTP/1.1 400 Bad Request\r\nServer: Apache-Coyote/1.1\r\nTransfer-Encoding: chunked\r\nDate: Wed, 04 Apr 2012 16:53:49 GMT\r\nConnection: close\r\n\r\n0\r\n\r\n">>, + Resp = <<"HTTP/1.1 400 Bad Request\r\nServer: Apache-Coyote/1.1\r\nContent-Length:5\r\nDate: Wed, 04 Apr 2012 16:53:49 GMT\r\n\r\nabcde">>, do_send(Sock, Sock_type, Resp); process_request(Sock, Sock_type, #request{method='HEAD',