Browse Source

Added support for accepting binaries as header names

pull/110/head
Chandrashekhar Mullaparthi 11 years ago
parent
commit
8d9ccc7a8b
4 changed files with 67 additions and 8 deletions
  1. +1
    -1
      src/ibrowse.erl
  2. +17
    -5
      src/ibrowse_http_client.erl
  3. +26
    -2
      test/ibrowse_test.erl
  4. +23
    -0
      test/ibrowse_test_server.erl

+ 1
- 1
src/ibrowse.erl View File

@ -150,7 +150,7 @@ stop() ->
%% The Status return value indicates the HTTP status code returned by the webserver %% The Status return value indicates the HTTP status code returned by the webserver
%% @spec send_req(Url::string(), Headers::headerList(), Method::method()) -> response() %% @spec send_req(Url::string(), Headers::headerList(), Method::method()) -> response()
%% headerList() = [{header(), value()}] %% headerList() = [{header(), value()}]
%% header() = atom() | string()
%% header() = atom() | string() | binary()
%% value() = term() %% value() = term()
%% method() = get | post | head | options | put | delete | trace | mkcol | propfind | proppatch | lock | unlock | move | copy %% method() = get | post | head | options | put | delete | trace | mkcol | propfind | proppatch | lock | unlock | move | copy
%% Status = string() %% Status = string()

+ 17
- 5
src/ibrowse_http_client.erl View File

@ -924,7 +924,7 @@ make_request(Method, Headers, AbsPath, RelPath, Body, Options,
HttpVsn = http_vsn_string(get_value(http_vsn, Options, {1,1})), HttpVsn = http_vsn_string(get_value(http_vsn, Options, {1,1})),
Fun1 = fun({X, Y}) when is_atom(X) -> Fun1 = fun({X, Y}) when is_atom(X) ->
{to_lower(atom_to_list(X)), X, Y}; {to_lower(atom_to_list(X)), X, Y};
({X, Y}) when is_list(X) ->
({X, Y}) when is_list(X); is_binary(X) ->
{to_lower(X), X, Y} {to_lower(X), X, Y}
end, end,
Headers_0 = [Fun1(X) || X <- Headers], Headers_0 = [Fun1(X) || X <- Headers],
@ -1010,7 +1010,7 @@ encode_headers(L) ->
encode_headers(L, []). encode_headers(L, []).
encode_headers([{http_vsn, _Val} | T], Acc) -> encode_headers([{http_vsn, _Val} | T], Acc) ->
encode_headers(T, Acc); encode_headers(T, Acc);
encode_headers([{Name,Val} | T], Acc) when is_list(Name) ->
encode_headers([{Name,Val} | T], Acc) when is_list(Name); is_binary(Name) ->
encode_headers(T, [[Name, ": ", fmt_val(Val), crnl()] | Acc]); encode_headers(T, [[Name, ": ", fmt_val(Val), crnl()] | Acc]);
encode_headers([{Name,Val} | T], Acc) when is_atom(Name) -> encode_headers([{Name,Val} | T], Acc) when is_atom(Name) ->
encode_headers(T, [[atom_to_list(Name), ": ", fmt_val(Val), crnl()] | Acc]); encode_headers(T, [[atom_to_list(Name), ": ", fmt_val(Val), crnl()] | Acc]);
@ -1072,7 +1072,7 @@ parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs,
{HttpVsn, StatCode, Headers_1, Status_line, Raw_headers} = parse_headers(Headers), {HttpVsn, StatCode, Headers_1, Status_line, Raw_headers} = parse_headers(Headers),
do_trace("HttpVsn: ~p StatusCode: ~p Headers_1 -> ~1000.p~n", [HttpVsn, StatCode, Headers_1]), do_trace("HttpVsn: ~p StatusCode: ~p Headers_1 -> ~1000.p~n", [HttpVsn, StatCode, Headers_1]),
LCHeaders = [{to_lower(X), Y} || {X,Y} <- Headers_1], LCHeaders = [{to_lower(X), Y} || {X,Y} <- Headers_1],
ConnClose = to_lower(get_value("connection", LCHeaders, "false")),
ConnClose = to_lower(get_header_value("connection", LCHeaders, "false")),
IsClosing = is_connection_closing(HttpVsn, ConnClose), IsClosing = is_connection_closing(HttpVsn, ConnClose),
State_0 = case IsClosing of State_0 = case IsClosing of
true -> true ->
@ -1096,11 +1096,11 @@ parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs,
http_status_code=StatCode} http_status_code=StatCode}
end, end,
put(conn_close, ConnClose), put(conn_close, ConnClose),
TransferEncodings = to_lower(get_value("transfer-encoding", LCHeaders, "false")),
TransferEncodings = to_lower(get_header_value("transfer-encoding", LCHeaders, "false")),
IsChunked = lists:any(fun(Enc) -> string:strip(Enc) =:= "chunked" end, IsChunked = lists:any(fun(Enc) -> string:strip(Enc) =:= "chunked" end,
string:tokens(TransferEncodings, ",")), string:tokens(TransferEncodings, ",")),
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
case get_header_value("content-length", LCHeaders, undefined) of
_ when Method == connect, _ when Method == connect,
hd(StatCode) == $2 -> hd(StatCode) == $2 ->
{_, Reqs_1} = queue:out(Reqs), {_, Reqs_1} = queue:out(Reqs),
@ -1906,6 +1906,8 @@ cancel_timer(Ref, {eat_message, Msg}) ->
make_req_id() -> make_req_id() ->
now(). now().
to_lower(Str) when is_binary(Str) ->
to_lower(binary_to_list(Str));
to_lower(Str) -> to_lower(Str) ->
to_lower(Str, []). to_lower(Str, []).
to_lower([H|T], Acc) when H >= $A, H =< $Z -> to_lower([H|T], Acc) when H >= $A, H =< $Z ->
@ -2021,3 +2023,13 @@ to_binary({X, _}) when is_function(X) -> to_binary(X);
to_binary(X) when is_function(X) -> <<"body generated by function">>; to_binary(X) when is_function(X) -> <<"body generated by function">>;
to_binary(X) when is_list(X) -> list_to_binary(X); to_binary(X) when is_list(X) -> list_to_binary(X);
to_binary(X) when is_binary(X) -> X. to_binary(X) when is_binary(X) -> X.
get_header_value(Name, Headers, Default_val) ->
case lists:keysearch(Name, 1, Headers) of
false ->
Default_val;
{value, {_, Val}} when is_binary(Val) ->
binary_to_list(Val);
{value, {_, Val}} ->
Val
end.

+ 26
- 2
test/ibrowse_test.erl View File

@ -32,6 +32,8 @@
test_303_response_with_no_body/1, test_303_response_with_no_body/1,
test_303_response_with_a_body/0, test_303_response_with_a_body/0,
test_303_response_with_a_body/1, test_303_response_with_a_body/1,
test_binary_headers/0,
test_binary_headers/1,
test_generate_body_0/0 test_generate_body_0/0
]). ]).
@ -239,8 +241,8 @@ dump_errors(Key, Iod) ->
{local_test_fun, test_pipeline_head_timeout, []}, {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, []}, {local_test_fun, test_head_response_with_body, []},
{local_test_fun, test_303_response_with_a_body, []}
{local_test_fun, test_303_response_with_a_body, []},
{local_test_fun, test_binary_headers, []}
]). ]).
unit_tests() -> unit_tests() ->
@ -466,6 +468,28 @@ test_head_transfer_encoding(Url) ->
{test_failed, Res} {test_failed, Res}
end. 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_binary_headers() ->
clear_msg_q(),
test_binary_headers("http://localhost:8181/ibrowse_echo_header").
test_binary_headers(Url) ->
case ibrowse:send_req(Url, [{<<"x-binary">>, <<"x-header">>}], get) of
{ok, "200", Headers, _} ->
case proplists:get_value("x-binary", Headers) of
"x-header" ->
success;
V ->
{fail, V}
end;
Res ->
{test_failed, Res}
end.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% Test what happens when the response to a HEAD request is a %% Test what happens when the response to a HEAD request is a
%% Chunked-Encoding response with a non-empty body. Issue #67 on %% Chunked-Encoding response with a non-empty body. Issue #67 on

+ 23
- 0
test/ibrowse_test_server.erl View File

@ -155,6 +155,25 @@ process_request(Sock, Sock_type,
uri = {abs_path, "/ibrowse_head_transfer_enc"}}) -> uri = {abs_path, "/ibrowse_head_transfer_enc"}}) ->
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">>, 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); do_send(Sock, Sock_type, Resp);
process_request(Sock, Sock_type,
#request{method='GET',
headers = Headers,
uri = {abs_path, "/ibrowse_echo_header"}}) ->
Tag = "x-binary",
Headers_1 = [{to_lower(X), to_lower(Y)} || {http_header, _, X, _, Y} <- Headers],
X_binary_header_val = case lists:keysearch(Tag, 1, Headers_1) of
false ->
"not_found";
{value, {_, V}} ->
V
end,
Resp = [<<"HTTP/1.1 200 OK\r\n">>,
<<"Server: ibrowse_test\r\n">>,
Tag, ": ", X_binary_header_val, "\r\n",
<<"Content-Length: 0\r\n\r\n">>],
do_send(Sock, Sock_type, Resp);
process_request(Sock, Sock_type, process_request(Sock, Sock_type,
#request{method='HEAD', #request{method='HEAD',
headers = _Headers, headers = _Headers,
@ -231,3 +250,7 @@ split_list_at(List2, 0, List1) ->
split_list_at([H | List2], N, List1) -> split_list_at([H | List2], N, List1) ->
split_list_at(List2, N-1, [H | List1]). split_list_at(List2, N-1, [H | List1]).
to_lower(X) when is_atom(X) ->
list_to_atom(to_lower(atom_to_list(X)));
to_lower(X) when is_list(X) ->
string:to_lower(X).

Loading…
Cancel
Save