Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: cmullaparthi/ibrowse
base: d8c2e5a8da
...
head fork: cmullaparthi/ibrowse
compare: 52ce596b91
Checking mergeability… Don't worry, you can still create the pull request.
  • 2 commits
  • 8 files changed
  • 1 commit comment
  • 2 contributors
Commits on Aug 02, 2012
chandru Fixed bug in using proxy ca3b547
Commits on Aug 03, 2012
@cmullaparthi Bug fix for issue #67 52ce596
View
6 CHANGELOG
@@ -1,5 +1,11 @@
CONTRIBUTIONS & CHANGE HISTORY
==============================
+03-08-2012 - v4.0.0
+ * Fixed a regression in handling HEAD.
+ https://github.com/cmullaparthi/ibrowse/issues/67
+
+ * Fixed a bug in handling SSL requests through a proxy
+
06-04-2012 - v3.0.4
* Fix for the following issue
https://github.com/cmullaparthi/ibrowse/issues/67
View
1  CONTRIBUTORS
@@ -24,6 +24,7 @@ Karol Skocik
Konstantin Nikiforov
Kostis Sagonas
Matthew Reilly
+Michael Terry
Oscar Hellstr?m
Paul J. Davis
Peter Kristensen
View
2  README.md
@@ -7,7 +7,7 @@ ibrowse is a HTTP client written in erlang.
**Comments to:** chandrashekhar.mullaparthi@gmail.com
-**Current Version:** 3.0.4
+**Current Version:** 4.0.0
**Latest Version:** git://github.com/cmullaparthi/ibrowse.git
View
2  src/ibrowse.app.src
@@ -1,6 +1,6 @@
{application, ibrowse,
[{description, "Erlang HTTP client application"},
- {vsn, "3.0.4"},
+ {vsn, "4.0.0"},
{modules, [ ibrowse,
ibrowse_http_client,
ibrowse_app,
View
3  src/ibrowse.erl
@@ -285,7 +285,8 @@ send_req(Url, Headers, Method, Body) ->
%% {transfer_encoding, {chunked, ChunkSize}} |
%% {headers_as_is, boolean()} |
%% {give_raw_headers, boolean()} |
-%% {preserve_chunked_encoding,boolean()}
+%% {preserve_chunked_encoding,boolean()} |
+%% {workaround, head_response_with_body}
%%
%% stream_to() = process() | {process(), once}
%% process() = pid() | atom()
View
60 src/ibrowse_http_client.erl
@@ -1012,43 +1012,45 @@ parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs,
LCHeaders = [{to_lower(X), Y} || {X,Y} <- Headers_1],
ConnClose = to_lower(get_value("connection", LCHeaders, "false")),
IsClosing = is_connection_closing(HttpVsn, ConnClose),
- case IsClosing of
- true ->
- shutting_down(State);
- false ->
- ok
- end,
+ State_0 = case IsClosing of
+ 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#state{recvd_headers=Headers_1, status=get_body,
- reply_buffer = <<>>,
- status_line = Status_line,
- raw_headers = Raw_headers,
- http_status_code=StatCode, is_closing=IsClosing};
+ State_0#state{recvd_headers=Headers_1, status=get_body,
+ reply_buffer = <<>>,
+ status_line = Status_line,
+ raw_headers = Raw_headers,
+ http_status_code=StatCode};
false ->
- State#state{recvd_headers=Headers_1, status=get_body,
- reply_buffer = <<>>,
- http_status_code=StatCode, is_closing=IsClosing}
+ State_0#state{recvd_headers=Headers_1, status=get_body,
+ 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),
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#state{reqs = Reqs_1,
- recvd_headers = [],
- status = idle
- }));
+ upgrade_to_ssl(set_cur_request(State_0#state{reqs = Reqs_1,
+ 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,
- TransferEncoding =/= "chunked" ->
+ _ 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,
@@ -1062,6 +1064,15 @@ parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs,
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 ->
+ {_, 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 hd(StatCode) =:= $1 ->
%% No message body is expected. Server may send
%% one or more 1XX responses before a proper
@@ -1821,9 +1832,12 @@ inc_pipeline_counter(#state{lb_ets_tid = undefined} = State) ->
State;
inc_pipeline_counter(#state{cur_pipeline_size = Pipe_sz,
lb_ets_tid = Tid} = State) ->
- ets:update_counter(Tid, self(), {2,1,99999,9999}),
+ update_counter(Tid, self(), {2,1,99999,9999}),
State#state{cur_pipeline_size = Pipe_sz + 1}.
+update_counter(Tid, Key, Args) ->
+ ets:update_counter(Tid, Key, Args).
+
dec_pipeline_counter(#state{is_closing = true} = State) ->
State;
dec_pipeline_counter(#state{lb_ets_tid = undefined} = State) ->
@@ -1831,8 +1845,8 @@ dec_pipeline_counter(#state{lb_ets_tid = undefined} = State) ->
dec_pipeline_counter(#state{cur_pipeline_size = Pipe_sz,
lb_ets_tid = Tid} = State) ->
try
- ets:update_counter(Tid, self(), {2,-1,0,0}),
- ets:update_counter(Tid, self(), {3,-1,0,0})
+ update_counter(Tid, self(), {2,-1,0,0}),
+ update_counter(Tid, self(), {3,-1,0,0})
catch
_:_ ->
ok
View
27 src/ibrowse_test.erl
@@ -25,7 +25,9 @@
test_pipeline_head_timeout/1,
do_test_pipeline_head_timeout/4,
test_head_transfer_encoding/0,
- test_head_transfer_encoding/1
+ test_head_transfer_encoding/1,
+ test_head_response_with_body/0,
+ test_head_response_with_body/1
]).
test_stream_once(Url, Method, Options) ->
@@ -230,7 +232,8 @@ dump_errors(Key, Iod) ->
{"https://github.com", get, [{ssl_options, [{depth, 2}]}]},
{local_test_fun, test_20122010, []},
{local_test_fun, test_pipeline_head_timeout, []},
- {local_test_fun, test_head_transfer_encoding, []}
+ {local_test_fun, test_head_transfer_encoding, []},
+ {local_test_fun, test_head_response_with_body, []}
]).
unit_tests() ->
@@ -446,15 +449,33 @@ log_msg(Fmt, Args) ->
%% ------------------------------------------------------------------------------
test_head_transfer_encoding() ->
clear_msg_q(),
- test_head_transfer_encoding("http://localhost:8181/ibrowse_head_transfer_enc").
+ test_head_transfer_encoding("http://localhost:8181/ibrowse_head_test").
test_head_transfer_encoding(Url) ->
case ibrowse:send_req(Url, [], head) of
+ {ok, "200", _, _} ->
+ success;
+ Res ->
+ {test_failed, Res}
+ end.
+
+%%------------------------------------------------------------------------------
+%% Test what happens when the response to a HEAD request is a
+%% Chunked-Encoding response with a non-empty body. Issue #67 on
+%% Github
+%% ------------------------------------------------------------------------------
+test_head_response_with_body() ->
+ clear_msg_q(),
+ test_head_response_with_body("http://localhost:8181/ibrowse_head_transfer_enc").
+
+test_head_response_with_body(Url) ->
+ case ibrowse:send_req(Url, [], head, [], [{workaround, head_response_with_body}]) of
{ok, "400", _, _} ->
success;
Res ->
{test_failed, Res}
end.
+
%%------------------------------------------------------------------------------
%% Test what happens when the request at the head of a pipeline times out
%%------------------------------------------------------------------------------
View
6 test/ibrowse_test_server.erl
@@ -153,6 +153,12 @@ process_request(Sock, Sock_type,
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">>,
do_send(Sock, Sock_type, Resp);
+process_request(Sock, Sock_type,
+ #request{method='HEAD',
+ headers = _Headers,
+ uri = {abs_path, "/ibrowse_head_test"}}) ->
+ Resp = <<"HTTP/1.1 200 OK\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\n">>,
+ do_send(Sock, Sock_type, Resp);
process_request(Sock, Sock_type, Req) ->
do_trace("Recvd req: ~p~n", [Req]),
Resp = <<"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n">>,

Showing you all comments on commits in this comparison.

@seth

I'm confused, it looks to me like the code block is identical for Method =:= head, Head_response_with_body =:= true and the following Method =:= head block. Haven't tested the build yet, but at least on master the code under both clauses is identical.

Something went wrong with that request. Please try again.