aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2013-09-02 20:05:03 +0200
committerLoïc Hoguin <[email protected]>2013-09-02 20:05:03 +0200
commit67410731e09592fbc1b8658ef9da10ba43f4650d (patch)
tree743cd2f7b149a746b00d5df56f4ec047899da7a6
parent9eab26d8353f1546ad8209196c36e42d616f952e (diff)
parentd2adbf3de66f15dc0b654c76ee7bee7bd9c8c778 (diff)
downloadcowboy-67410731e09592fbc1b8658ef9da10ba43f4650d.tar.gz
cowboy-67410731e09592fbc1b8658ef9da10ba43f4650d.tar.bz2
cowboy-67410731e09592fbc1b8658ef9da10ba43f4650d.zip
Merge branch 'ipv6-literal' of git://github.com/yamt/cowboy
-rw-r--r--src/cowboy_client.erl17
-rw-r--r--src/cowboy_protocol.erl89
-rw-r--r--src/cowboy_spdy.erl2
-rw-r--r--test/http_SUITE.erl44
-rw-r--r--test/http_SUITE_data/http_req_attr.erl19
5 files changed, 131 insertions, 40 deletions
diff --git a/src/cowboy_client.erl b/src/cowboy_client.erl
index b5f96b3..10aaa9c 100644
--- a/src/cowboy_client.erl
+++ b/src/cowboy_client.erl
@@ -93,16 +93,19 @@ request(Method, URL, Headers, Body, Client=#client{
end,
VersionBin = atom_to_binary(Version, latin1),
%% @todo do keepalive too, allow override...
- Headers2 = [
- {<<"host">>, FullHost},
+ Headers2 = case lists:keyfind(<<"host">>, 1, Headers) of
+ false -> [{<<"host">>, FullHost}|Headers];
+ _ -> Headers
+ end,
+ Headers3 = [
{<<"user-agent">>, <<"Cow">>}
- |Headers],
- Headers3 = case iolist_size(Body) of
- 0 -> Headers2;
- Length -> [{<<"content-length">>, integer_to_list(Length)}|Headers2]
+ |Headers2],
+ Headers4 = case iolist_size(Body) of
+ 0 -> Headers3;
+ Length -> [{<<"content-length">>, integer_to_list(Length)}|Headers3]
end,
HeadersData = [[Name, <<": ">>, Value, <<"\r\n">>]
- || {Name, Value} <- Headers3],
+ || {Name, Value} <- Headers4],
Data = [Method, <<" ">>, Path, <<" ">>, VersionBin, <<"\r\n">>,
HeadersData, <<"\r\n">>, Body],
raw_request(Data, Client2).
diff --git a/src/cowboy_protocol.erl b/src/cowboy_protocol.erl
index 40be2c0..68a03b1 100644
--- a/src/cowboy_protocol.erl
+++ b/src/cowboy_protocol.erl
@@ -54,7 +54,7 @@
%% Internal.
-export([init/4]).
-export([parse_request/3]).
--export([parse_host/2]).
+-export([parse_host/3]).
-export([resume/6]).
-type opts() :: [{compress, boolean()}
@@ -426,7 +426,7 @@ request(B, State=#state{transport=Transport}, M, P, Q, Version, Headers) ->
request(B, State, M, P, Q, Version, Headers,
<<>>, default_port(Transport:name()));
{_, RawHost} ->
- case catch parse_host(RawHost, <<>>) of
+ case catch parse_host(RawHost, false, <<>>) of
{'EXIT', _} ->
error_terminate(400, State);
{Host, undefined} ->
@@ -443,39 +443,43 @@ default_port(ssl) -> 443;
default_port(_) -> 80.
%% Another hurtful block of code. :)
-parse_host(<<>>, Acc) ->
+parse_host(<< $[, Rest/bits >>, false, <<>>) ->
+ parse_host(Rest, true, << $[ >>);
+parse_host(<<>>, false, Acc) ->
{Acc, undefined};
-parse_host(<< $:, Rest/bits >>, Acc) ->
+parse_host(<< $:, Rest/bits >>, false, Acc) ->
{Acc, list_to_integer(binary_to_list(Rest))};
-parse_host(<< C, Rest/bits >>, Acc) ->
+parse_host(<< $], Rest/bits >>, true, Acc) ->
+ parse_host(Rest, false, << Acc/binary, $] >>);
+parse_host(<< C, Rest/bits >>, E, Acc) ->
case C of
- $A -> parse_host(Rest, << Acc/binary, $a >>);
- $B -> parse_host(Rest, << Acc/binary, $b >>);
- $C -> parse_host(Rest, << Acc/binary, $c >>);
- $D -> parse_host(Rest, << Acc/binary, $d >>);
- $E -> parse_host(Rest, << Acc/binary, $e >>);
- $F -> parse_host(Rest, << Acc/binary, $f >>);
- $G -> parse_host(Rest, << Acc/binary, $g >>);
- $H -> parse_host(Rest, << Acc/binary, $h >>);
- $I -> parse_host(Rest, << Acc/binary, $i >>);
- $J -> parse_host(Rest, << Acc/binary, $j >>);
- $K -> parse_host(Rest, << Acc/binary, $k >>);
- $L -> parse_host(Rest, << Acc/binary, $l >>);
- $M -> parse_host(Rest, << Acc/binary, $m >>);
- $N -> parse_host(Rest, << Acc/binary, $n >>);
- $O -> parse_host(Rest, << Acc/binary, $o >>);
- $P -> parse_host(Rest, << Acc/binary, $p >>);
- $Q -> parse_host(Rest, << Acc/binary, $q >>);
- $R -> parse_host(Rest, << Acc/binary, $r >>);
- $S -> parse_host(Rest, << Acc/binary, $s >>);
- $T -> parse_host(Rest, << Acc/binary, $t >>);
- $U -> parse_host(Rest, << Acc/binary, $u >>);
- $V -> parse_host(Rest, << Acc/binary, $v >>);
- $W -> parse_host(Rest, << Acc/binary, $w >>);
- $X -> parse_host(Rest, << Acc/binary, $x >>);
- $Y -> parse_host(Rest, << Acc/binary, $y >>);
- $Z -> parse_host(Rest, << Acc/binary, $z >>);
- _ -> parse_host(Rest, << Acc/binary, C >>)
+ $A -> parse_host(Rest, E, << Acc/binary, $a >>);
+ $B -> parse_host(Rest, E, << Acc/binary, $b >>);
+ $C -> parse_host(Rest, E, << Acc/binary, $c >>);
+ $D -> parse_host(Rest, E, << Acc/binary, $d >>);
+ $E -> parse_host(Rest, E, << Acc/binary, $e >>);
+ $F -> parse_host(Rest, E, << Acc/binary, $f >>);
+ $G -> parse_host(Rest, E, << Acc/binary, $g >>);
+ $H -> parse_host(Rest, E, << Acc/binary, $h >>);
+ $I -> parse_host(Rest, E, << Acc/binary, $i >>);
+ $J -> parse_host(Rest, E, << Acc/binary, $j >>);
+ $K -> parse_host(Rest, E, << Acc/binary, $k >>);
+ $L -> parse_host(Rest, E, << Acc/binary, $l >>);
+ $M -> parse_host(Rest, E, << Acc/binary, $m >>);
+ $N -> parse_host(Rest, E, << Acc/binary, $n >>);
+ $O -> parse_host(Rest, E, << Acc/binary, $o >>);
+ $P -> parse_host(Rest, E, << Acc/binary, $p >>);
+ $Q -> parse_host(Rest, E, << Acc/binary, $q >>);
+ $R -> parse_host(Rest, E, << Acc/binary, $r >>);
+ $S -> parse_host(Rest, E, << Acc/binary, $s >>);
+ $T -> parse_host(Rest, E, << Acc/binary, $t >>);
+ $U -> parse_host(Rest, E, << Acc/binary, $u >>);
+ $V -> parse_host(Rest, E, << Acc/binary, $v >>);
+ $W -> parse_host(Rest, E, << Acc/binary, $w >>);
+ $X -> parse_host(Rest, E, << Acc/binary, $x >>);
+ $Y -> parse_host(Rest, E, << Acc/binary, $y >>);
+ $Z -> parse_host(Rest, E, << Acc/binary, $z >>);
+ _ -> parse_host(Rest, E, << Acc/binary, C >>)
end.
%% End of request parsing.
@@ -589,3 +593,24 @@ error_terminate(Status, Req, State) ->
terminate(#state{socket=Socket, transport=Transport}) ->
Transport:close(Socket),
ok.
+
+%% Tests.
+
+-ifdef(TEST).
+
+parse_host(RawHost) ->
+ parse_host(RawHost, false, <<>>).
+
+parse_host_test() ->
+ {<<"example.org">>, 8080} = parse_host(<<"example.org:8080">>),
+ {<<"example.org">>, undefined} = parse_host(<<"example.org">>),
+ {<<"192.0.2.1">>, 8080} = parse_host(<<"192.0.2.1:8080">>),
+ {<<"192.0.2.1">>, undefined} = parse_host(<<"192.0.2.1">>),
+ {<<"[2001:db8::1]">>, 8080} = parse_host(<<"[2001:db8::1]:8080">>),
+ {<<"[2001:db8::1]">>, undefined} = parse_host(<<"[2001:db8::1]">>),
+ {<<"[::ffff:192.0.2.1]">>, 8080} =
+ parse_host(<<"[::ffff:192.0.2.1]:8080">>),
+ {<<"[::ffff:192.0.2.1]">>, undefined} =
+ parse_host(<<"[::ffff:192.0.2.1]">>).
+
+-endif.
diff --git a/src/cowboy_spdy.erl b/src/cowboy_spdy.erl
index 425a422..0ecbf17 100644
--- a/src/cowboy_spdy.erl
+++ b/src/cowboy_spdy.erl
@@ -346,7 +346,7 @@ data_from_file(Socket, Transport, StreamID, IoDevice) ->
request_init(FakeSocket, Peer, OnRequest, OnResponse,
Env, Middlewares, Method, Host, Path, Version, Headers) ->
Version2 = parse_version(Version),
- {Host2, Port} = cowboy_protocol:parse_host(Host, <<>>),
+ {Host2, Port} = cowboy_protocol:parse_host(Host, false, <<>>),
{Path2, Query} = parse_path(Path, <<>>),
Req = cowboy_req:new(FakeSocket, ?MODULE, Peer,
Method, Path2, Query, Version2, Headers,
diff --git a/test/http_SUITE.erl b/test/http_SUITE.erl
index 2135fdc..f784c50 100644
--- a/test/http_SUITE.erl
+++ b/test/http_SUITE.erl
@@ -51,6 +51,7 @@
-export([onresponse_capitalize/1]).
-export([onresponse_crash/1]).
-export([onresponse_reply/1]).
+-export([parse_host/1]).
-export([pipeline/1]).
-export([pipeline_long_polling/1]).
-export([rest_bad_accept/1]).
@@ -103,6 +104,7 @@ all() ->
{group, onrequest},
{group, onresponse},
{group, onresponse_capitalize},
+ {group, parse_host},
{group, set_env}
].
@@ -184,6 +186,9 @@ groups() ->
{onresponse_capitalize, [parallel], [
onresponse_capitalize
]},
+ {parse_host, [], [
+ parse_host
+ ]},
{set_env, [], [
set_env_dispatch
]}
@@ -297,6 +302,22 @@ init_per_group(onresponse_capitalize, Config) ->
{ok, Client} = cowboy_client:init([]),
[{scheme, <<"http">>}, {port, Port}, {opts, []},
{transport, Transport}, {client, Client}|Config];
+init_per_group(parse_host, Config) ->
+ Transport = ranch_tcp,
+ Dispatch = cowboy_router:compile([
+ {'_', [
+ {"/req_attr", http_req_attr, []}
+ ]}
+ ]),
+ {ok, _} = cowboy:start_http(http, 100, [{port, 0}], [
+ {env, [{dispatch, Dispatch}]},
+ {max_keepalive, 50},
+ {timeout, 500}
+ ]),
+ Port = ranch:get_port(http),
+ {ok, Client} = cowboy_client:init([]),
+ [{scheme, <<"http">>}, {port, Port}, {opts, []},
+ {transport, Transport}, {client, Client}|Config];
init_per_group(set_env, Config) ->
Transport = ranch_tcp,
{ok, _} = cowboy:start_http(set_env, 100, [{port, 0}], [
@@ -802,6 +823,29 @@ onresponse_hook(_, Headers, _, Req) ->
<<"777 Lucky">>, [{<<"x-hook">>, <<"onresponse">>}|Headers], Req),
Req2.
+parse_host(Config) ->
+ Tests = [
+ {<<"example.org\n8080">>, <<"example.org:8080">>},
+ {<<"example.org\n80">>, <<"example.org">>},
+ {<<"192.0.2.1\n8080">>, <<"192.0.2.1:8080">>},
+ {<<"192.0.2.1\n80">>, <<"192.0.2.1">>},
+ {<<"[2001:db8::1]\n8080">>, <<"[2001:db8::1]:8080">>},
+ {<<"[2001:db8::1]\n80">>, <<"[2001:db8::1]">>},
+ {<<"[::ffff:192.0.2.1]\n8080">>, <<"[::ffff:192.0.2.1]:8080">>},
+ {<<"[::ffff:192.0.2.1]\n80">>, <<"[::ffff:192.0.2.1]">>}
+ ],
+ [begin
+ Client = ?config(client, Config),
+ {ok, Client2} = cowboy_client:request(<<"GET">>,
+ build_url("/req_attr?attr=host_and_port", Config),
+ [{<<"host">>, Host}],
+ Client),
+ {ok, 200, _, Client3} = cowboy_client:response(Client2),
+ {ok, Value, Client4} = cowboy_client:response_body(Client3),
+ {error, closed} = cowboy_client:response(Client4),
+ Value
+ end || {Value, Host} <- Tests].
+
pipeline(Config) ->
Client = ?config(client, Config),
{ok, Client2} = cowboy_client:request(<<"GET">>,
diff --git a/test/http_SUITE_data/http_req_attr.erl b/test/http_SUITE_data/http_req_attr.erl
new file mode 100644
index 0000000..eb5e70e
--- /dev/null
+++ b/test/http_SUITE_data/http_req_attr.erl
@@ -0,0 +1,19 @@
+%% Feel free to use, reuse and abuse the code in this file.
+
+-module(http_req_attr).
+-behaviour(cowboy_http_handler).
+-export([init/3, handle/2, terminate/3]).
+
+init({_, http}, Req, _) ->
+ {Attr, Req2} = cowboy_req:qs_val(<<"attr">>, Req),
+ {ok, Req2, Attr}.
+
+handle(Req, <<"host_and_port">> = Attr) ->
+ {Host, Req2} = cowboy_req:host(Req),
+ {Port, Req3} = cowboy_req:port(Req2),
+ Value = [Host, "\n", integer_to_list(Port)],
+ {ok, Req4} = cowboy_req:reply(200, [], Value, Req3),
+ {ok, Req4, Attr}.
+
+terminate(_, _, _) ->
+ ok.