0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-15 04:30:25 +00:00

Merge branch 'ipv6-literal' of git://github.com/yamt/cowboy

This commit is contained in:
Loïc Hoguin 2013-09-02 20:05:03 +02:00
commit 67410731e0
5 changed files with 131 additions and 40 deletions

View file

@ -93,16 +93,19 @@ request(Method, URL, Headers, Body, Client=#client{
end, end,
VersionBin = atom_to_binary(Version, latin1), VersionBin = atom_to_binary(Version, latin1),
%% @todo do keepalive too, allow override... %% @todo do keepalive too, allow override...
Headers2 = [ Headers2 = case lists:keyfind(<<"host">>, 1, Headers) of
{<<"host">>, FullHost}, false -> [{<<"host">>, FullHost}|Headers];
_ -> Headers
end,
Headers3 = [
{<<"user-agent">>, <<"Cow">>} {<<"user-agent">>, <<"Cow">>}
|Headers], |Headers2],
Headers3 = case iolist_size(Body) of Headers4 = case iolist_size(Body) of
0 -> Headers2; 0 -> Headers3;
Length -> [{<<"content-length">>, integer_to_list(Length)}|Headers2] Length -> [{<<"content-length">>, integer_to_list(Length)}|Headers3]
end, end,
HeadersData = [[Name, <<": ">>, Value, <<"\r\n">>] HeadersData = [[Name, <<": ">>, Value, <<"\r\n">>]
|| {Name, Value} <- Headers3], || {Name, Value} <- Headers4],
Data = [Method, <<" ">>, Path, <<" ">>, VersionBin, <<"\r\n">>, Data = [Method, <<" ">>, Path, <<" ">>, VersionBin, <<"\r\n">>,
HeadersData, <<"\r\n">>, Body], HeadersData, <<"\r\n">>, Body],
raw_request(Data, Client2). raw_request(Data, Client2).

View file

@ -54,7 +54,7 @@
%% Internal. %% Internal.
-export([init/4]). -export([init/4]).
-export([parse_request/3]). -export([parse_request/3]).
-export([parse_host/2]). -export([parse_host/3]).
-export([resume/6]). -export([resume/6]).
-type opts() :: [{compress, boolean()} -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, request(B, State, M, P, Q, Version, Headers,
<<>>, default_port(Transport:name())); <<>>, default_port(Transport:name()));
{_, RawHost} -> {_, RawHost} ->
case catch parse_host(RawHost, <<>>) of case catch parse_host(RawHost, false, <<>>) of
{'EXIT', _} -> {'EXIT', _} ->
error_terminate(400, State); error_terminate(400, State);
{Host, undefined} -> {Host, undefined} ->
@ -443,39 +443,43 @@ default_port(ssl) -> 443;
default_port(_) -> 80. default_port(_) -> 80.
%% Another hurtful block of code. :) %% Another hurtful block of code. :)
parse_host(<<>>, Acc) -> parse_host(<< $[, Rest/bits >>, false, <<>>) ->
parse_host(Rest, true, << $[ >>);
parse_host(<<>>, false, Acc) ->
{Acc, undefined}; {Acc, undefined};
parse_host(<< $:, Rest/bits >>, Acc) -> parse_host(<< $:, Rest/bits >>, false, Acc) ->
{Acc, list_to_integer(binary_to_list(Rest))}; {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 case C of
$A -> parse_host(Rest, << Acc/binary, $a >>); $A -> parse_host(Rest, E, << Acc/binary, $a >>);
$B -> parse_host(Rest, << Acc/binary, $b >>); $B -> parse_host(Rest, E, << Acc/binary, $b >>);
$C -> parse_host(Rest, << Acc/binary, $c >>); $C -> parse_host(Rest, E, << Acc/binary, $c >>);
$D -> parse_host(Rest, << Acc/binary, $d >>); $D -> parse_host(Rest, E, << Acc/binary, $d >>);
$E -> parse_host(Rest, << Acc/binary, $e >>); $E -> parse_host(Rest, E, << Acc/binary, $e >>);
$F -> parse_host(Rest, << Acc/binary, $f >>); $F -> parse_host(Rest, E, << Acc/binary, $f >>);
$G -> parse_host(Rest, << Acc/binary, $g >>); $G -> parse_host(Rest, E, << Acc/binary, $g >>);
$H -> parse_host(Rest, << Acc/binary, $h >>); $H -> parse_host(Rest, E, << Acc/binary, $h >>);
$I -> parse_host(Rest, << Acc/binary, $i >>); $I -> parse_host(Rest, E, << Acc/binary, $i >>);
$J -> parse_host(Rest, << Acc/binary, $j >>); $J -> parse_host(Rest, E, << Acc/binary, $j >>);
$K -> parse_host(Rest, << Acc/binary, $k >>); $K -> parse_host(Rest, E, << Acc/binary, $k >>);
$L -> parse_host(Rest, << Acc/binary, $l >>); $L -> parse_host(Rest, E, << Acc/binary, $l >>);
$M -> parse_host(Rest, << Acc/binary, $m >>); $M -> parse_host(Rest, E, << Acc/binary, $m >>);
$N -> parse_host(Rest, << Acc/binary, $n >>); $N -> parse_host(Rest, E, << Acc/binary, $n >>);
$O -> parse_host(Rest, << Acc/binary, $o >>); $O -> parse_host(Rest, E, << Acc/binary, $o >>);
$P -> parse_host(Rest, << Acc/binary, $p >>); $P -> parse_host(Rest, E, << Acc/binary, $p >>);
$Q -> parse_host(Rest, << Acc/binary, $q >>); $Q -> parse_host(Rest, E, << Acc/binary, $q >>);
$R -> parse_host(Rest, << Acc/binary, $r >>); $R -> parse_host(Rest, E, << Acc/binary, $r >>);
$S -> parse_host(Rest, << Acc/binary, $s >>); $S -> parse_host(Rest, E, << Acc/binary, $s >>);
$T -> parse_host(Rest, << Acc/binary, $t >>); $T -> parse_host(Rest, E, << Acc/binary, $t >>);
$U -> parse_host(Rest, << Acc/binary, $u >>); $U -> parse_host(Rest, E, << Acc/binary, $u >>);
$V -> parse_host(Rest, << Acc/binary, $v >>); $V -> parse_host(Rest, E, << Acc/binary, $v >>);
$W -> parse_host(Rest, << Acc/binary, $w >>); $W -> parse_host(Rest, E, << Acc/binary, $w >>);
$X -> parse_host(Rest, << Acc/binary, $x >>); $X -> parse_host(Rest, E, << Acc/binary, $x >>);
$Y -> parse_host(Rest, << Acc/binary, $y >>); $Y -> parse_host(Rest, E, << Acc/binary, $y >>);
$Z -> parse_host(Rest, << Acc/binary, $z >>); $Z -> parse_host(Rest, E, << Acc/binary, $z >>);
_ -> parse_host(Rest, << Acc/binary, C >>) _ -> parse_host(Rest, E, << Acc/binary, C >>)
end. end.
%% End of request parsing. %% End of request parsing.
@ -589,3 +593,24 @@ error_terminate(Status, Req, State) ->
terminate(#state{socket=Socket, transport=Transport}) -> terminate(#state{socket=Socket, transport=Transport}) ->
Transport:close(Socket), Transport:close(Socket),
ok. 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.

View file

@ -346,7 +346,7 @@ data_from_file(Socket, Transport, StreamID, IoDevice) ->
request_init(FakeSocket, Peer, OnRequest, OnResponse, request_init(FakeSocket, Peer, OnRequest, OnResponse,
Env, Middlewares, Method, Host, Path, Version, Headers) -> Env, Middlewares, Method, Host, Path, Version, Headers) ->
Version2 = parse_version(Version), Version2 = parse_version(Version),
{Host2, Port} = cowboy_protocol:parse_host(Host, <<>>), {Host2, Port} = cowboy_protocol:parse_host(Host, false, <<>>),
{Path2, Query} = parse_path(Path, <<>>), {Path2, Query} = parse_path(Path, <<>>),
Req = cowboy_req:new(FakeSocket, ?MODULE, Peer, Req = cowboy_req:new(FakeSocket, ?MODULE, Peer,
Method, Path2, Query, Version2, Headers, Method, Path2, Query, Version2, Headers,

View file

@ -51,6 +51,7 @@
-export([onresponse_capitalize/1]). -export([onresponse_capitalize/1]).
-export([onresponse_crash/1]). -export([onresponse_crash/1]).
-export([onresponse_reply/1]). -export([onresponse_reply/1]).
-export([parse_host/1]).
-export([pipeline/1]). -export([pipeline/1]).
-export([pipeline_long_polling/1]). -export([pipeline_long_polling/1]).
-export([rest_bad_accept/1]). -export([rest_bad_accept/1]).
@ -103,6 +104,7 @@ all() ->
{group, onrequest}, {group, onrequest},
{group, onresponse}, {group, onresponse},
{group, onresponse_capitalize}, {group, onresponse_capitalize},
{group, parse_host},
{group, set_env} {group, set_env}
]. ].
@ -184,6 +186,9 @@ groups() ->
{onresponse_capitalize, [parallel], [ {onresponse_capitalize, [parallel], [
onresponse_capitalize onresponse_capitalize
]}, ]},
{parse_host, [], [
parse_host
]},
{set_env, [], [ {set_env, [], [
set_env_dispatch set_env_dispatch
]} ]}
@ -297,6 +302,22 @@ init_per_group(onresponse_capitalize, Config) ->
{ok, Client} = cowboy_client:init([]), {ok, Client} = cowboy_client:init([]),
[{scheme, <<"http">>}, {port, Port}, {opts, []}, [{scheme, <<"http">>}, {port, Port}, {opts, []},
{transport, Transport}, {client, Client}|Config]; {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) -> init_per_group(set_env, Config) ->
Transport = ranch_tcp, Transport = ranch_tcp,
{ok, _} = cowboy:start_http(set_env, 100, [{port, 0}], [ {ok, _} = cowboy:start_http(set_env, 100, [{port, 0}], [
@ -802,6 +823,29 @@ onresponse_hook(_, Headers, _, Req) ->
<<"777 Lucky">>, [{<<"x-hook">>, <<"onresponse">>}|Headers], Req), <<"777 Lucky">>, [{<<"x-hook">>, <<"onresponse">>}|Headers], Req),
Req2. 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) -> pipeline(Config) ->
Client = ?config(client, Config), Client = ?config(client, Config),
{ok, Client2} = cowboy_client:request(<<"GET">>, {ok, Client2} = cowboy_client:request(<<"GET">>,

View file

@ -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.