mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
Add request body support for SPDY
And various other improvements following the addition of two tests. New dependency cowlib that will gradually receive most of the parse code from SPDY but also HTTP and its headers.
This commit is contained in:
parent
d68b3de9d9
commit
9eab26d835
4 changed files with 186 additions and 425 deletions
6
Makefile
6
Makefile
|
@ -10,9 +10,11 @@ PLT_APPS = crypto public_key ssl
|
||||||
|
|
||||||
# Dependencies.
|
# Dependencies.
|
||||||
|
|
||||||
DEPS = ranch
|
DEPS = cowlib ranch
|
||||||
TEST_DEPS = ct_helper gun
|
dep_cowlib = pkg://cowlib master
|
||||||
dep_ranch = pkg://ranch 0.8.5
|
dep_ranch = pkg://ranch 0.8.5
|
||||||
|
|
||||||
|
TEST_DEPS = ct_helper gun
|
||||||
dep_ct_helper = https://github.com/extend/ct_helper.git master
|
dep_ct_helper = https://github.com/extend/ct_helper.git master
|
||||||
dep_gun = pkg://gun master
|
dep_gun = pkg://gun master
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
-export([system_code_change/4]).
|
-export([system_code_change/4]).
|
||||||
|
|
||||||
%% Internal request process.
|
%% Internal request process.
|
||||||
-export([request_init/9]).
|
-export([request_init/11]).
|
||||||
-export([resume/5]).
|
-export([resume/5]).
|
||||||
-export([reply/4]).
|
-export([reply/4]).
|
||||||
-export([stream_reply/3]).
|
-export([stream_reply/3]).
|
||||||
|
@ -41,6 +41,7 @@
|
||||||
|
|
||||||
%% Internal transport functions.
|
%% Internal transport functions.
|
||||||
-export([name/0]).
|
-export([name/0]).
|
||||||
|
-export([recv/3]).
|
||||||
-export([send/2]).
|
-export([send/2]).
|
||||||
-export([sendfile/2]).
|
-export([sendfile/2]).
|
||||||
|
|
||||||
|
@ -48,6 +49,8 @@
|
||||||
streamid :: non_neg_integer(),
|
streamid :: non_neg_integer(),
|
||||||
pid :: pid(),
|
pid :: pid(),
|
||||||
input = nofin :: fin | nofin,
|
input = nofin :: fin | nofin,
|
||||||
|
in_buffer = <<>> :: binary(),
|
||||||
|
is_recv = false :: {true, non_neg_integer()} | false,
|
||||||
output = nofin :: fin | nofin
|
output = nofin :: fin | nofin
|
||||||
}).
|
}).
|
||||||
|
|
||||||
|
@ -67,19 +70,9 @@
|
||||||
children = [] :: [#child{}]
|
children = [] :: [#child{}]
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-record(special_headers, {
|
|
||||||
method,
|
|
||||||
path,
|
|
||||||
version,
|
|
||||||
host,
|
|
||||||
scheme %% @todo We don't use it.
|
|
||||||
}).
|
|
||||||
|
|
||||||
-type opts() :: [].
|
-type opts() :: [].
|
||||||
-export_type([opts/0]).
|
-export_type([opts/0]).
|
||||||
|
|
||||||
-include("cowboy_spdy.hrl").
|
|
||||||
|
|
||||||
%% API.
|
%% API.
|
||||||
|
|
||||||
%% @doc Start a SPDY protocol process.
|
%% @doc Start a SPDY protocol process.
|
||||||
|
@ -108,41 +101,59 @@ init(Parent, Ref, Socket, Transport, Opts) ->
|
||||||
Env = [{listener, Ref}|get_value(env, Opts, [])],
|
Env = [{listener, Ref}|get_value(env, Opts, [])],
|
||||||
OnRequest = get_value(onrequest, Opts, undefined),
|
OnRequest = get_value(onrequest, Opts, undefined),
|
||||||
OnResponse = get_value(onresponse, Opts, undefined),
|
OnResponse = get_value(onresponse, Opts, undefined),
|
||||||
Zdef = zlib:open(),
|
Zdef = cow_spdy:deflate_init(),
|
||||||
ok = zlib:deflateInit(Zdef),
|
Zinf = cow_spdy:inflate_init(),
|
||||||
_ = zlib:deflateSetDictionary(Zdef, ?ZDICT),
|
|
||||||
Zinf = zlib:open(),
|
|
||||||
ok = zlib:inflateInit(Zinf),
|
|
||||||
ok = ranch:accept_ack(Ref),
|
ok = ranch:accept_ack(Ref),
|
||||||
loop(#state{parent=Parent, socket=Socket, transport=Transport,
|
loop(#state{parent=Parent, socket=Socket, transport=Transport,
|
||||||
middlewares=Middlewares, env=Env, onrequest=OnRequest,
|
middlewares=Middlewares, env=Env, onrequest=OnRequest,
|
||||||
onresponse=OnResponse, peer=Peer, zdef=Zdef, zinf=Zinf}).
|
onresponse=OnResponse, peer=Peer, zdef=Zdef, zinf=Zinf}).
|
||||||
|
|
||||||
loop(State=#state{parent=Parent, socket=Socket, transport=Transport,
|
loop(State=#state{parent=Parent, socket=Socket, transport=Transport,
|
||||||
buffer=Buffer, children=Children}) ->
|
buffer=Buffer, zinf=Zinf, children=Children}) ->
|
||||||
{OK, Closed, Error} = Transport:messages(),
|
{OK, Closed, Error} = Transport:messages(),
|
||||||
Transport:setopts(Socket, [{active, once}]),
|
Transport:setopts(Socket, [{active, once}]),
|
||||||
receive
|
receive
|
||||||
{OK, Socket, Data} ->
|
{OK, Socket, Data} ->
|
||||||
Data2 = << Buffer/binary, Data/binary >>,
|
Data2 = << Buffer/binary, Data/binary >>,
|
||||||
case Data2 of
|
case cow_spdy:split(Data2) of
|
||||||
<< _:40, Length:24, _/bits >>
|
{true, Frame, Rest} ->
|
||||||
when byte_size(Data2) >= Length + 8 ->
|
P = cow_spdy:parse(Frame, Zinf),
|
||||||
Length2 = Length + 8,
|
handle_frame(State#state{buffer=Rest}, P);
|
||||||
<< Frame:Length2/binary, Rest/bits >> = Data2,
|
false ->
|
||||||
control_frame(State#state{buffer=Rest}, Frame);
|
loop(State#state{buffer=Data2})
|
||||||
Rest ->
|
|
||||||
loop(State#state{buffer=Rest})
|
|
||||||
end;
|
end;
|
||||||
{Closed, Socket} ->
|
{Closed, Socket} ->
|
||||||
terminate(State);
|
terminate(State);
|
||||||
{Error, Socket, _Reason} ->
|
{Error, Socket, _Reason} ->
|
||||||
terminate(State);
|
terminate(State);
|
||||||
|
%% @todo Timeout (send a message to self).
|
||||||
|
{recv, FromSocket = {Pid, StreamID}, FromPid, Length, _Timeout}
|
||||||
|
when Pid =:= self() ->
|
||||||
|
Child = #child{in_buffer=InBuffer, is_recv=false}
|
||||||
|
= lists:keyfind(StreamID, #child.streamid, Children),
|
||||||
|
if
|
||||||
|
Length =:= 0, InBuffer =/= <<>> ->
|
||||||
|
FromPid ! {recv, FromSocket, {ok, InBuffer}},
|
||||||
|
Children2 = lists:keyreplace(StreamID, #child.streamid,
|
||||||
|
Children, Child#child{in_buffer= <<>>}),
|
||||||
|
loop(State#state{children=Children2});
|
||||||
|
byte_size(InBuffer) >= Length ->
|
||||||
|
<< Data:Length/binary, Rest/binary >> = InBuffer,
|
||||||
|
FromPid ! {recv, FromSocket, {ok, Data}},
|
||||||
|
Children2 = lists:keyreplace(StreamID, #child.streamid,
|
||||||
|
Children, Child#child{in_buffer=Rest}),
|
||||||
|
loop(State#state{children=Children2});
|
||||||
|
true ->
|
||||||
|
Children2 = lists:keyreplace(StreamID, #child.streamid,
|
||||||
|
Children, Child#child{is_recv=
|
||||||
|
{true, FromSocket, FromPid, Length}}),
|
||||||
|
loop(State#state{children=Children2})
|
||||||
|
end;
|
||||||
{reply, {Pid, StreamID}, Status, Headers}
|
{reply, {Pid, StreamID}, Status, Headers}
|
||||||
when Pid =:= self() ->
|
when Pid =:= self() ->
|
||||||
Child = #child{output=nofin} = lists:keyfind(StreamID,
|
Child = #child{output=nofin} = lists:keyfind(StreamID,
|
||||||
#child.streamid, Children),
|
#child.streamid, Children),
|
||||||
syn_reply(State, fin, StreamID, Status, Headers),
|
syn_reply(State, StreamID, true, Status, Headers),
|
||||||
Children2 = lists:keyreplace(StreamID,
|
Children2 = lists:keyreplace(StreamID,
|
||||||
#child.streamid, Children, Child#child{output=fin}),
|
#child.streamid, Children, Child#child{output=fin}),
|
||||||
loop(State#state{children=Children2});
|
loop(State#state{children=Children2});
|
||||||
|
@ -150,8 +161,8 @@ loop(State=#state{parent=Parent, socket=Socket, transport=Transport,
|
||||||
when Pid =:= self() ->
|
when Pid =:= self() ->
|
||||||
Child = #child{output=nofin} = lists:keyfind(StreamID,
|
Child = #child{output=nofin} = lists:keyfind(StreamID,
|
||||||
#child.streamid, Children),
|
#child.streamid, Children),
|
||||||
syn_reply(State, nofin, StreamID, Status, Headers),
|
syn_reply(State, StreamID, false, Status, Headers),
|
||||||
data(State, fin, StreamID, Body),
|
data(State, StreamID, true, Body),
|
||||||
Children2 = lists:keyreplace(StreamID,
|
Children2 = lists:keyreplace(StreamID,
|
||||||
#child.streamid, Children, Child#child{output=fin}),
|
#child.streamid, Children, Child#child{output=fin}),
|
||||||
loop(State#state{children=Children2});
|
loop(State#state{children=Children2});
|
||||||
|
@ -159,19 +170,19 @@ loop(State=#state{parent=Parent, socket=Socket, transport=Transport,
|
||||||
when Pid =:= self() ->
|
when Pid =:= self() ->
|
||||||
#child{output=nofin} = lists:keyfind(StreamID,
|
#child{output=nofin} = lists:keyfind(StreamID,
|
||||||
#child.streamid, Children),
|
#child.streamid, Children),
|
||||||
syn_reply(State, nofin, StreamID, Status, Headers),
|
syn_reply(State, StreamID, false, Status, Headers),
|
||||||
loop(State);
|
loop(State);
|
||||||
{stream_data, {Pid, StreamID}, Data}
|
{stream_data, {Pid, StreamID}, Data}
|
||||||
when Pid =:= self() ->
|
when Pid =:= self() ->
|
||||||
#child{output=nofin} = lists:keyfind(StreamID,
|
#child{output=nofin} = lists:keyfind(StreamID,
|
||||||
#child.streamid, Children),
|
#child.streamid, Children),
|
||||||
data(State, nofin, StreamID, Data),
|
data(State, StreamID, false, Data),
|
||||||
loop(State);
|
loop(State);
|
||||||
{stream_close, {Pid, StreamID}}
|
{stream_close, {Pid, StreamID}}
|
||||||
when Pid =:= self() ->
|
when Pid =:= self() ->
|
||||||
Child = #child{output=nofin} = lists:keyfind(StreamID,
|
Child = #child{output=nofin} = lists:keyfind(StreamID,
|
||||||
#child.streamid, Children),
|
#child.streamid, Children),
|
||||||
data(State, fin, StreamID),
|
data(State, StreamID, true, <<>>),
|
||||||
Children2 = lists:keyreplace(StreamID,
|
Children2 = lists:keyreplace(StreamID,
|
||||||
#child.streamid, Children, Child#child{output=fin}),
|
#child.streamid, Children, Child#child{output=fin}),
|
||||||
loop(State#state{children=Children2});
|
loop(State#state{children=Children2});
|
||||||
|
@ -186,6 +197,7 @@ loop(State=#state{parent=Parent, socket=Socket, transport=Transport,
|
||||||
{'EXIT', Parent, Reason} ->
|
{'EXIT', Parent, Reason} ->
|
||||||
exit(Reason);
|
exit(Reason);
|
||||||
{'EXIT', Pid, _} ->
|
{'EXIT', Pid, _} ->
|
||||||
|
%% @todo Report the error if any.
|
||||||
Children2 = lists:keydelete(Pid, #child.pid, Children),
|
Children2 = lists:keydelete(Pid, #child.pid, Children),
|
||||||
loop(State#state{children=Children2});
|
loop(State#state{children=Children2});
|
||||||
{system, From, Request} ->
|
{system, From, Request} ->
|
||||||
|
@ -220,231 +232,95 @@ system_terminate(Reason, _, _, _) ->
|
||||||
system_code_change(Misc, _, _, _) ->
|
system_code_change(Misc, _, _, _) ->
|
||||||
{ok, Misc}.
|
{ok, Misc}.
|
||||||
|
|
||||||
%% We do not support SYN_STREAM with FLAG_UNIDIRECTIONAL set.
|
%% FLAG_UNIDIRECTIONAL can only be set by the server.
|
||||||
control_frame(State, << 1:1, 3:15, 1:16, _:6, 1:1, _:26,
|
handle_frame(State, {syn_stream, StreamID, _, _, true,
|
||||||
StreamID:31, _/bits >>) ->
|
_, _, _, _, _, _, _}) ->
|
||||||
|
rst_stream(State, StreamID, protocol_error),
|
||||||
|
loop(State);
|
||||||
|
%% We do not support Associated-To-Stream-ID.
|
||||||
|
handle_frame(State, {syn_stream, StreamID, AssocToStreamID,
|
||||||
|
_, _, _, _, _, _, _, _, _}) when AssocToStreamID =/= 0 ->
|
||||||
rst_stream(State, StreamID, internal_error),
|
rst_stream(State, StreamID, internal_error),
|
||||||
loop(State);
|
loop(State);
|
||||||
%% We do not support Associated-To-Stream-ID and CREDENTIAL Slot.
|
%% SYN_STREAM.
|
||||||
control_frame(State, << 1:1, 3:15, 1:16, _:33, StreamID:31, _:1,
|
|
||||||
AssocToStreamID:31, _:8, Slot:8, _/bits >>)
|
|
||||||
when AssocToStreamID =/= 0; Slot =/= 0 ->
|
|
||||||
rst_stream(State, StreamID, internal_error),
|
|
||||||
loop(State);
|
|
||||||
%% SYN_STREAM
|
|
||||||
%%
|
%%
|
||||||
%% Erlang does not allow us to control the priority of processes
|
%% Erlang does not allow us to control the priority of processes
|
||||||
%% so we ignore that value entirely.
|
%% so we ignore that value entirely.
|
||||||
control_frame(State=#state{middlewares=Middlewares, env=Env,
|
handle_frame(State=#state{middlewares=Middlewares, env=Env,
|
||||||
onrequest=OnRequest, onresponse=OnResponse, peer=Peer,
|
onrequest=OnRequest, onresponse=OnResponse, peer=Peer,
|
||||||
zinf=Zinf, children=Children},
|
children=Children}, {syn_stream, StreamID, _, IsFin,
|
||||||
<< 1:1, 3:15, 1:16, Flags:8, _:25, StreamID:31,
|
_, _, Method, _, Host, Path, Version, Headers}) ->
|
||||||
_:32, _Priority:3, _:13, Rest/bits >>) ->
|
Pid = spawn_link(?MODULE, request_init, [
|
||||||
IsFin = case Flags of
|
{self(), StreamID}, Peer, OnRequest, OnResponse,
|
||||||
1 -> fin;
|
Env, Middlewares, Method, Host, Path, Version, Headers
|
||||||
0 -> nofin
|
]),
|
||||||
end,
|
IsFin2 = if IsFin -> fin; true -> nofin end,
|
||||||
[<< NbHeaders:32, Rest2/bits >>] = try
|
loop(State#state{last_streamid=StreamID,
|
||||||
zlib:inflate(Zinf, Rest)
|
children=[#child{streamid=StreamID, pid=Pid,
|
||||||
catch _:_ ->
|
input=IsFin2, output=nofin}|Children]});
|
||||||
ok = zlib:inflateSetDictionary(Zinf, ?ZDICT),
|
%% RST_STREAM.
|
||||||
zlib:inflate(Zinf, <<>>)
|
handle_frame(State, {rst_stream, StreamID, Status}) ->
|
||||||
end,
|
error_logger:error_msg("Received RST_STREAM frame ~p ~p",
|
||||||
case syn_stream_headers(Rest2, NbHeaders, [], #special_headers{}) of
|
[StreamID, Status]),
|
||||||
{ok, Headers, Special} ->
|
|
||||||
Pid = spawn_link(?MODULE, request_init,
|
|
||||||
[self(), StreamID, Peer, Headers,
|
|
||||||
OnRequest, OnResponse, Env, Middlewares, Special]),
|
|
||||||
loop(State#state{last_streamid=StreamID,
|
|
||||||
children=[#child{streamid=StreamID, pid=Pid,
|
|
||||||
input=IsFin, output=nofin}|Children]});
|
|
||||||
{error, badname} ->
|
|
||||||
rst_stream(State, StreamID, protocol_error),
|
|
||||||
loop(State#state{last_streamid=StreamID});
|
|
||||||
{error, special} ->
|
|
||||||
rst_stream(State, StreamID, protocol_error),
|
|
||||||
loop(State#state{last_streamid=StreamID})
|
|
||||||
end;
|
|
||||||
%% SYN_REPLY
|
|
||||||
control_frame(State, << 1:1, 3:15, 2:16, _/bits >>) ->
|
|
||||||
error_logger:error_msg("Ignored SYN_REPLY control frame~n"),
|
|
||||||
loop(State);
|
|
||||||
%% RST_STREAM
|
|
||||||
control_frame(State, << 1:1, 3:15, 3:16, _Flags:8, _Length:24,
|
|
||||||
_:1, _StreamID:31, StatusCode:32 >>) ->
|
|
||||||
Status = case StatusCode of
|
|
||||||
1 -> protocol_error;
|
|
||||||
2 -> invalid_stream;
|
|
||||||
3 -> refused_stream;
|
|
||||||
4 -> unsupported_version;
|
|
||||||
5 -> cancel;
|
|
||||||
6 -> internal_error;
|
|
||||||
7 -> flow_control_error;
|
|
||||||
8 -> stream_in_use;
|
|
||||||
9 -> stream_already_closed;
|
|
||||||
10 -> invalid_credentials;
|
|
||||||
11 -> frame_too_large
|
|
||||||
end,
|
|
||||||
error_logger:error_msg("Received RST_STREAM control frame: ~p~n", [Status]),
|
|
||||||
%% @todo Stop StreamID.
|
%% @todo Stop StreamID.
|
||||||
loop(State);
|
loop(State);
|
||||||
%% SETTINGS
|
%% PING initiated by the server; ignore, we don't send any.
|
||||||
control_frame(State, << 1:1, 3:15, 4:16, 0:8, _:24,
|
handle_frame(State, {ping, PingID}) when PingID rem 2 =:= 0 ->
|
||||||
NbEntries:32, Rest/bits >>) ->
|
|
||||||
Settings = [begin
|
|
||||||
Name = case ID of
|
|
||||||
1 -> upload_bandwidth;
|
|
||||||
2 -> download_bandwidth;
|
|
||||||
3 -> round_trip_time;
|
|
||||||
4 -> max_concurrent_streams;
|
|
||||||
5 -> current_cwnd;
|
|
||||||
6 -> download_retrans_rate;
|
|
||||||
7 -> initial_window_size;
|
|
||||||
8 -> client_certificate_vector_size
|
|
||||||
end,
|
|
||||||
{Flags, Name, Value}
|
|
||||||
end || << Flags:8, ID:24, Value:32 >> <= Rest],
|
|
||||||
if
|
|
||||||
NbEntries =/= length(Settings) ->
|
|
||||||
goaway(State, protocol_error),
|
|
||||||
terminate(State);
|
|
||||||
true ->
|
|
||||||
error_logger:error_msg("Ignored SETTINGS control frame: ~p~n",
|
|
||||||
[Settings]),
|
|
||||||
loop(State)
|
|
||||||
end;
|
|
||||||
%% PING initiated by the server; ignore, we don't send any
|
|
||||||
control_frame(State, << 1:1, 3:15, 6:16, 0:8, 4:24, PingID:32 >>)
|
|
||||||
when PingID rem 2 =:= 0 ->
|
|
||||||
error_logger:error_msg("Ignored PING control frame: ~p~n", [PingID]),
|
error_logger:error_msg("Ignored PING control frame: ~p~n", [PingID]),
|
||||||
loop(State);
|
loop(State);
|
||||||
%% PING initiated by the client; send it back
|
%% PING initiated by the client; send it back.
|
||||||
control_frame(State=#state{socket=Socket, transport=Transport},
|
handle_frame(State=#state{socket=Socket, transport=Transport},
|
||||||
Data = << 1:1, 3:15, 6:16, 0:8, 4:24, _:32 >>) ->
|
{ping, PingID}) ->
|
||||||
Transport:send(Socket, Data),
|
Transport:send(Socket, cow_spdy:ping(PingID)),
|
||||||
loop(State);
|
loop(State);
|
||||||
%% GOAWAY
|
%% Data received for a stream.
|
||||||
control_frame(State, << 1:1, 3:15, 7:16, _/bits >>) ->
|
handle_frame(State=#state{children=Children},
|
||||||
error_logger:error_msg("Ignored GOAWAY control frame~n"),
|
{data, StreamID, IsFin, Data}) ->
|
||||||
loop(State);
|
Child = #child{input=nofin, in_buffer=Buffer, is_recv=IsRecv}
|
||||||
%% HEADERS
|
= lists:keyfind(StreamID, #child.streamid, Children),
|
||||||
control_frame(State, << 1:1, 3:15, 8:16, _/bits >>) ->
|
Data2 = << Buffer/binary, Data/binary >>,
|
||||||
error_logger:error_msg("Ignored HEADERS control frame~n"),
|
IsFin2 = if IsFin -> fin; true -> nofin end,
|
||||||
loop(State);
|
Child2 = case IsRecv of
|
||||||
%% WINDOW_UPDATE
|
{true, FromSocket, FromPid, 0} ->
|
||||||
control_frame(State, << 1:1, 3:15, 9:16, 0:8, _/bits >>) ->
|
FromPid ! {recv, FromSocket, {ok, Data2}},
|
||||||
error_logger:error_msg("Ignored WINDOW_UPDATE control frame~n"),
|
Child#child{input=IsFin2, in_buffer= <<>>, is_recv=false};
|
||||||
loop(State);
|
{true, FromSocket, FromPid, Length} when byte_size(Data2) >= Length ->
|
||||||
%% CREDENTIAL
|
<< Data3:Length/binary, Rest/binary >> = Data2,
|
||||||
control_frame(State, << 1:1, 3:15, 10:16, _/bits >>) ->
|
FromPid ! {recv, FromSocket, {ok, Data3}},
|
||||||
error_logger:error_msg("Ignored CREDENTIAL control frame~n"),
|
Child#child{input=IsFin2, in_buffer=Rest, is_recv=false};
|
||||||
loop(State);
|
_ ->
|
||||||
%% ???
|
Child#child{input=IsFin2, in_buffer=Data2}
|
||||||
control_frame(State, _) ->
|
end,
|
||||||
|
Children2 = lists:keyreplace(StreamID, #child.streamid, Children, Child2),
|
||||||
|
loop(State#state{children=Children2});
|
||||||
|
%% General error, can't recover.
|
||||||
|
handle_frame(State, {error, badprotocol}) ->
|
||||||
goaway(State, protocol_error),
|
goaway(State, protocol_error),
|
||||||
terminate(State).
|
terminate(State);
|
||||||
|
%% Ignore all other frames for now.
|
||||||
|
handle_frame(State, Frame) ->
|
||||||
|
error_logger:error_msg("Ignored frame ~p", [Frame]),
|
||||||
|
loop(State).
|
||||||
|
|
||||||
%% @todo We must wait for the children to finish here,
|
%% @todo We must wait for the children to finish here,
|
||||||
%% but only up to N milliseconds. Then we shutdown.
|
%% but only up to N milliseconds. Then we shutdown.
|
||||||
terminate(_State) ->
|
terminate(_State) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
syn_stream_headers(<<>>, 0, Acc, Special=#special_headers{
|
|
||||||
method=Method, path=Path, version=Version, host=Host, scheme=Scheme}) ->
|
|
||||||
if
|
|
||||||
Method =:= undefined; Path =:= undefined; Version =:= undefined;
|
|
||||||
Host =:= undefined; Scheme =:= undefined ->
|
|
||||||
{error, special};
|
|
||||||
true ->
|
|
||||||
{ok, lists:reverse(Acc), Special}
|
|
||||||
end;
|
|
||||||
syn_stream_headers(<< 0:32, _Rest/bits >>, _NbHeaders, _Acc, _Special) ->
|
|
||||||
{error, badname};
|
|
||||||
syn_stream_headers(<< NameLen:32, Rest/bits >>, NbHeaders, Acc, Special) ->
|
|
||||||
<< Name:NameLen/binary, ValueLen:32, Rest2/bits >> = Rest,
|
|
||||||
<< Value:ValueLen/binary, Rest3/bits >> = Rest2,
|
|
||||||
case Name of
|
|
||||||
<<":host">> ->
|
|
||||||
syn_stream_headers(Rest3, NbHeaders - 1,
|
|
||||||
[{<<"host">>, Value}|Acc],
|
|
||||||
Special#special_headers{host=Value});
|
|
||||||
<<":method">> ->
|
|
||||||
syn_stream_headers(Rest3, NbHeaders - 1, Acc,
|
|
||||||
Special#special_headers{method=Value});
|
|
||||||
<<":path">> ->
|
|
||||||
syn_stream_headers(Rest3, NbHeaders - 1, Acc,
|
|
||||||
Special#special_headers{path=Value});
|
|
||||||
<<":version">> ->
|
|
||||||
syn_stream_headers(Rest3, NbHeaders - 1, Acc,
|
|
||||||
Special#special_headers{version=Value});
|
|
||||||
<<":scheme">> ->
|
|
||||||
syn_stream_headers(Rest3, NbHeaders - 1, Acc,
|
|
||||||
Special#special_headers{scheme=Value});
|
|
||||||
_ ->
|
|
||||||
syn_stream_headers(Rest3, NbHeaders - 1,
|
|
||||||
[{Name, Value}|Acc], Special)
|
|
||||||
end.
|
|
||||||
|
|
||||||
syn_reply(#state{socket=Socket, transport=Transport, zdef=Zdef},
|
syn_reply(#state{socket=Socket, transport=Transport, zdef=Zdef},
|
||||||
IsFin, StreamID, Status, Headers) ->
|
StreamID, IsFin, Status, Headers) ->
|
||||||
Headers2 = [{<<":status">>, Status},
|
Transport:send(Socket, cow_spdy:syn_reply(Zdef, StreamID, IsFin,
|
||||||
{<<":version">>, <<"HTTP/1.1">>}|Headers],
|
Status, <<"HTTP/1.1">>, Headers)).
|
||||||
NbHeaders = length(Headers2),
|
|
||||||
HeaderBlock = [begin
|
|
||||||
NameLen = byte_size(Name),
|
|
||||||
ValueLen = iolist_size(Value),
|
|
||||||
[<< NameLen:32, Name/binary, ValueLen:32 >>, Value]
|
|
||||||
end || {Name, Value} <- Headers2],
|
|
||||||
HeaderBlock2 = [<< NbHeaders:32 >>, HeaderBlock],
|
|
||||||
HeaderBlock3 = zlib:deflate(Zdef, HeaderBlock2, full),
|
|
||||||
Flags = case IsFin of
|
|
||||||
fin -> 1;
|
|
||||||
nofin -> 0
|
|
||||||
end,
|
|
||||||
Len = 4 + iolist_size(HeaderBlock3),
|
|
||||||
Transport:send(Socket, [
|
|
||||||
<< 1:1, 3:15, 2:16, Flags:8, Len:24, 0:1, StreamID:31 >>,
|
|
||||||
HeaderBlock3]).
|
|
||||||
|
|
||||||
rst_stream(#state{socket=Socket, transport=Transport}, StreamID, Status) ->
|
rst_stream(#state{socket=Socket, transport=Transport}, StreamID, Status) ->
|
||||||
StatusCode = case Status of
|
Transport:send(Socket, cow_spdy:rst_stream(StreamID, Status)).
|
||||||
protocol_error -> 1;
|
|
||||||
%% invalid_stream -> 2;
|
|
||||||
%% refused_stream -> 3;
|
|
||||||
%% unsupported_version -> 4;
|
|
||||||
%% cancel -> 5;
|
|
||||||
internal_error -> 6
|
|
||||||
%% flow_control_error -> 7;
|
|
||||||
%% stream_in_use -> 8;
|
|
||||||
%% stream_already_closed -> 9;
|
|
||||||
%% invalid_credentials -> 10;
|
|
||||||
%% frame_too_large -> 11
|
|
||||||
end,
|
|
||||||
Transport:send(Socket, << 1:1, 3:15, 3:16, 0:8, 8:24,
|
|
||||||
0:1, StreamID:31, StatusCode:32 >>).
|
|
||||||
|
|
||||||
goaway(#state{socket=Socket, transport=Transport, last_streamid=LastStreamID},
|
goaway(#state{socket=Socket, transport=Transport, last_streamid=LastStreamID},
|
||||||
Status) ->
|
Status) ->
|
||||||
StatusCode = case Status of
|
Transport:send(Socket, cow_spdy:goaway(LastStreamID, Status)).
|
||||||
ok -> 0;
|
|
||||||
protocol_error -> 1
|
|
||||||
%% internal_error -> 2
|
|
||||||
end,
|
|
||||||
Transport:send(Socket, << 1:1, 3:15, 7:16, 0:8, 8:24,
|
|
||||||
0:1, LastStreamID:31, StatusCode:32 >>).
|
|
||||||
|
|
||||||
data(#state{socket=Socket, transport=Transport}, fin, StreamID) ->
|
data(#state{socket=Socket, transport=Transport}, StreamID, IsFin, Data) ->
|
||||||
Transport:send(Socket, << 0:1, StreamID:31, 1:8, 0:24 >>).
|
Transport:send(Socket, cow_spdy:data(StreamID, IsFin, Data)).
|
||||||
|
|
||||||
data(#state{socket=Socket, transport=Transport}, IsFin, StreamID, Data) ->
|
|
||||||
Flags = case IsFin of
|
|
||||||
fin -> 1;
|
|
||||||
nofin -> 0
|
|
||||||
end,
|
|
||||||
Len = iolist_size(Data),
|
|
||||||
Transport:send(Socket, [
|
|
||||||
<< 0:1, StreamID:31, Flags:8, Len:24 >>,
|
|
||||||
Data]).
|
|
||||||
|
|
||||||
data_from_file(#state{socket=Socket, transport=Transport},
|
data_from_file(#state{socket=Socket, transport=Transport},
|
||||||
StreamID, Filepath) ->
|
StreamID, Filepath) ->
|
||||||
|
@ -454,12 +330,10 @@ data_from_file(#state{socket=Socket, transport=Transport},
|
||||||
data_from_file(Socket, Transport, StreamID, IoDevice) ->
|
data_from_file(Socket, Transport, StreamID, IoDevice) ->
|
||||||
case file:read(IoDevice, 16#1fff) of
|
case file:read(IoDevice, 16#1fff) of
|
||||||
eof ->
|
eof ->
|
||||||
_ = Transport:send(Socket, << 0:1, StreamID:31, 1:8, 0:24 >>),
|
_ = Transport:send(Socket, cow_spdy:data(StreamID, true, <<>>)),
|
||||||
ok;
|
ok;
|
||||||
{ok, Data} ->
|
{ok, Data} ->
|
||||||
Len = byte_size(Data),
|
case Transport:send(Socket, cow_spdy:data(StreamID, false, Data)) of
|
||||||
Data2 = [<< 0:1, StreamID:31, 0:8, Len:24 >>, Data],
|
|
||||||
case Transport:send(Socket, Data2) of
|
|
||||||
ok ->
|
ok ->
|
||||||
data_from_file(Socket, Transport, StreamID, IoDevice);
|
data_from_file(Socket, Transport, StreamID, IoDevice);
|
||||||
{error, _} ->
|
{error, _} ->
|
||||||
|
@ -469,14 +343,12 @@ data_from_file(Socket, Transport, StreamID, IoDevice) ->
|
||||||
|
|
||||||
%% Request process.
|
%% Request process.
|
||||||
|
|
||||||
request_init(Parent, StreamID, Peer,
|
request_init(FakeSocket, Peer, OnRequest, OnResponse,
|
||||||
Headers, OnRequest, OnResponse, Env, Middlewares,
|
Env, Middlewares, Method, Host, Path, Version, Headers) ->
|
||||||
#special_headers{method=Method, path=Path, version=Version,
|
|
||||||
host=Host}) ->
|
|
||||||
Version2 = parse_version(Version),
|
Version2 = parse_version(Version),
|
||||||
{Host2, Port} = cowboy_protocol:parse_host(Host, <<>>),
|
{Host2, Port} = cowboy_protocol:parse_host(Host, <<>>),
|
||||||
{Path2, Query} = parse_path(Path, <<>>),
|
{Path2, Query} = parse_path(Path, <<>>),
|
||||||
Req = cowboy_req:new({Parent, StreamID}, ?MODULE, Peer,
|
Req = cowboy_req:new(FakeSocket, ?MODULE, Peer,
|
||||||
Method, Path2, Query, Version2, Headers,
|
Method, Path2, Query, Version2, Headers,
|
||||||
Host2, Port, <<>>, true, false, OnResponse),
|
Host2, Port, <<>>, true, false, OnResponse),
|
||||||
case OnRequest of
|
case OnRequest of
|
||||||
|
@ -562,16 +434,23 @@ stream_close(Socket = {Pid, _}) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%% Internal transport functions.
|
%% Internal transport functions.
|
||||||
%% @todo recv
|
|
||||||
|
|
||||||
name() ->
|
name() ->
|
||||||
spdy.
|
spdy.
|
||||||
|
|
||||||
|
recv(Socket = {Pid, _}, Length, Timeout) ->
|
||||||
|
_ = Pid ! {recv, Socket, self(), Length, Timeout},
|
||||||
|
receive
|
||||||
|
{recv, Socket, Ret} ->
|
||||||
|
Ret
|
||||||
|
end.
|
||||||
|
|
||||||
send(Socket, Data) ->
|
send(Socket, Data) ->
|
||||||
stream_data(Socket, Data).
|
stream_data(Socket, Data).
|
||||||
|
|
||||||
%% We don't wait for the result of the actual sendfile call,
|
%% We don't wait for the result of the actual sendfile call,
|
||||||
%% therefore we can't know how much was actually sent.
|
%% therefore we can't know how much was actually sent.
|
||||||
|
%% This isn't a problem as we don't use this value in Cowboy.
|
||||||
sendfile(Socket = {Pid, _}, Filepath) ->
|
sendfile(Socket = {Pid, _}, Filepath) ->
|
||||||
_ = Pid ! {sendfile, Socket, Filepath},
|
_ = Pid ! {sendfile, Socket, Filepath},
|
||||||
{ok, undefined}.
|
{ok, undefined}.
|
||||||
|
|
|
@ -1,181 +0,0 @@
|
||||||
%% Zlib dictionary.
|
|
||||||
|
|
||||||
-define(ZDICT, <<
|
|
||||||
16#00, 16#00, 16#00, 16#07, 16#6f, 16#70, 16#74, 16#69,
|
|
||||||
16#6f, 16#6e, 16#73, 16#00, 16#00, 16#00, 16#04, 16#68,
|
|
||||||
16#65, 16#61, 16#64, 16#00, 16#00, 16#00, 16#04, 16#70,
|
|
||||||
16#6f, 16#73, 16#74, 16#00, 16#00, 16#00, 16#03, 16#70,
|
|
||||||
16#75, 16#74, 16#00, 16#00, 16#00, 16#06, 16#64, 16#65,
|
|
||||||
16#6c, 16#65, 16#74, 16#65, 16#00, 16#00, 16#00, 16#05,
|
|
||||||
16#74, 16#72, 16#61, 16#63, 16#65, 16#00, 16#00, 16#00,
|
|
||||||
16#06, 16#61, 16#63, 16#63, 16#65, 16#70, 16#74, 16#00,
|
|
||||||
16#00, 16#00, 16#0e, 16#61, 16#63, 16#63, 16#65, 16#70,
|
|
||||||
16#74, 16#2d, 16#63, 16#68, 16#61, 16#72, 16#73, 16#65,
|
|
||||||
16#74, 16#00, 16#00, 16#00, 16#0f, 16#61, 16#63, 16#63,
|
|
||||||
16#65, 16#70, 16#74, 16#2d, 16#65, 16#6e, 16#63, 16#6f,
|
|
||||||
16#64, 16#69, 16#6e, 16#67, 16#00, 16#00, 16#00, 16#0f,
|
|
||||||
16#61, 16#63, 16#63, 16#65, 16#70, 16#74, 16#2d, 16#6c,
|
|
||||||
16#61, 16#6e, 16#67, 16#75, 16#61, 16#67, 16#65, 16#00,
|
|
||||||
16#00, 16#00, 16#0d, 16#61, 16#63, 16#63, 16#65, 16#70,
|
|
||||||
16#74, 16#2d, 16#72, 16#61, 16#6e, 16#67, 16#65, 16#73,
|
|
||||||
16#00, 16#00, 16#00, 16#03, 16#61, 16#67, 16#65, 16#00,
|
|
||||||
16#00, 16#00, 16#05, 16#61, 16#6c, 16#6c, 16#6f, 16#77,
|
|
||||||
16#00, 16#00, 16#00, 16#0d, 16#61, 16#75, 16#74, 16#68,
|
|
||||||
16#6f, 16#72, 16#69, 16#7a, 16#61, 16#74, 16#69, 16#6f,
|
|
||||||
16#6e, 16#00, 16#00, 16#00, 16#0d, 16#63, 16#61, 16#63,
|
|
||||||
16#68, 16#65, 16#2d, 16#63, 16#6f, 16#6e, 16#74, 16#72,
|
|
||||||
16#6f, 16#6c, 16#00, 16#00, 16#00, 16#0a, 16#63, 16#6f,
|
|
||||||
16#6e, 16#6e, 16#65, 16#63, 16#74, 16#69, 16#6f, 16#6e,
|
|
||||||
16#00, 16#00, 16#00, 16#0c, 16#63, 16#6f, 16#6e, 16#74,
|
|
||||||
16#65, 16#6e, 16#74, 16#2d, 16#62, 16#61, 16#73, 16#65,
|
|
||||||
16#00, 16#00, 16#00, 16#10, 16#63, 16#6f, 16#6e, 16#74,
|
|
||||||
16#65, 16#6e, 16#74, 16#2d, 16#65, 16#6e, 16#63, 16#6f,
|
|
||||||
16#64, 16#69, 16#6e, 16#67, 16#00, 16#00, 16#00, 16#10,
|
|
||||||
16#63, 16#6f, 16#6e, 16#74, 16#65, 16#6e, 16#74, 16#2d,
|
|
||||||
16#6c, 16#61, 16#6e, 16#67, 16#75, 16#61, 16#67, 16#65,
|
|
||||||
16#00, 16#00, 16#00, 16#0e, 16#63, 16#6f, 16#6e, 16#74,
|
|
||||||
16#65, 16#6e, 16#74, 16#2d, 16#6c, 16#65, 16#6e, 16#67,
|
|
||||||
16#74, 16#68, 16#00, 16#00, 16#00, 16#10, 16#63, 16#6f,
|
|
||||||
16#6e, 16#74, 16#65, 16#6e, 16#74, 16#2d, 16#6c, 16#6f,
|
|
||||||
16#63, 16#61, 16#74, 16#69, 16#6f, 16#6e, 16#00, 16#00,
|
|
||||||
16#00, 16#0b, 16#63, 16#6f, 16#6e, 16#74, 16#65, 16#6e,
|
|
||||||
16#74, 16#2d, 16#6d, 16#64, 16#35, 16#00, 16#00, 16#00,
|
|
||||||
16#0d, 16#63, 16#6f, 16#6e, 16#74, 16#65, 16#6e, 16#74,
|
|
||||||
16#2d, 16#72, 16#61, 16#6e, 16#67, 16#65, 16#00, 16#00,
|
|
||||||
16#00, 16#0c, 16#63, 16#6f, 16#6e, 16#74, 16#65, 16#6e,
|
|
||||||
16#74, 16#2d, 16#74, 16#79, 16#70, 16#65, 16#00, 16#00,
|
|
||||||
16#00, 16#04, 16#64, 16#61, 16#74, 16#65, 16#00, 16#00,
|
|
||||||
16#00, 16#04, 16#65, 16#74, 16#61, 16#67, 16#00, 16#00,
|
|
||||||
16#00, 16#06, 16#65, 16#78, 16#70, 16#65, 16#63, 16#74,
|
|
||||||
16#00, 16#00, 16#00, 16#07, 16#65, 16#78, 16#70, 16#69,
|
|
||||||
16#72, 16#65, 16#73, 16#00, 16#00, 16#00, 16#04, 16#66,
|
|
||||||
16#72, 16#6f, 16#6d, 16#00, 16#00, 16#00, 16#04, 16#68,
|
|
||||||
16#6f, 16#73, 16#74, 16#00, 16#00, 16#00, 16#08, 16#69,
|
|
||||||
16#66, 16#2d, 16#6d, 16#61, 16#74, 16#63, 16#68, 16#00,
|
|
||||||
16#00, 16#00, 16#11, 16#69, 16#66, 16#2d, 16#6d, 16#6f,
|
|
||||||
16#64, 16#69, 16#66, 16#69, 16#65, 16#64, 16#2d, 16#73,
|
|
||||||
16#69, 16#6e, 16#63, 16#65, 16#00, 16#00, 16#00, 16#0d,
|
|
||||||
16#69, 16#66, 16#2d, 16#6e, 16#6f, 16#6e, 16#65, 16#2d,
|
|
||||||
16#6d, 16#61, 16#74, 16#63, 16#68, 16#00, 16#00, 16#00,
|
|
||||||
16#08, 16#69, 16#66, 16#2d, 16#72, 16#61, 16#6e, 16#67,
|
|
||||||
16#65, 16#00, 16#00, 16#00, 16#13, 16#69, 16#66, 16#2d,
|
|
||||||
16#75, 16#6e, 16#6d, 16#6f, 16#64, 16#69, 16#66, 16#69,
|
|
||||||
16#65, 16#64, 16#2d, 16#73, 16#69, 16#6e, 16#63, 16#65,
|
|
||||||
16#00, 16#00, 16#00, 16#0d, 16#6c, 16#61, 16#73, 16#74,
|
|
||||||
16#2d, 16#6d, 16#6f, 16#64, 16#69, 16#66, 16#69, 16#65,
|
|
||||||
16#64, 16#00, 16#00, 16#00, 16#08, 16#6c, 16#6f, 16#63,
|
|
||||||
16#61, 16#74, 16#69, 16#6f, 16#6e, 16#00, 16#00, 16#00,
|
|
||||||
16#0c, 16#6d, 16#61, 16#78, 16#2d, 16#66, 16#6f, 16#72,
|
|
||||||
16#77, 16#61, 16#72, 16#64, 16#73, 16#00, 16#00, 16#00,
|
|
||||||
16#06, 16#70, 16#72, 16#61, 16#67, 16#6d, 16#61, 16#00,
|
|
||||||
16#00, 16#00, 16#12, 16#70, 16#72, 16#6f, 16#78, 16#79,
|
|
||||||
16#2d, 16#61, 16#75, 16#74, 16#68, 16#65, 16#6e, 16#74,
|
|
||||||
16#69, 16#63, 16#61, 16#74, 16#65, 16#00, 16#00, 16#00,
|
|
||||||
16#13, 16#70, 16#72, 16#6f, 16#78, 16#79, 16#2d, 16#61,
|
|
||||||
16#75, 16#74, 16#68, 16#6f, 16#72, 16#69, 16#7a, 16#61,
|
|
||||||
16#74, 16#69, 16#6f, 16#6e, 16#00, 16#00, 16#00, 16#05,
|
|
||||||
16#72, 16#61, 16#6e, 16#67, 16#65, 16#00, 16#00, 16#00,
|
|
||||||
16#07, 16#72, 16#65, 16#66, 16#65, 16#72, 16#65, 16#72,
|
|
||||||
16#00, 16#00, 16#00, 16#0b, 16#72, 16#65, 16#74, 16#72,
|
|
||||||
16#79, 16#2d, 16#61, 16#66, 16#74, 16#65, 16#72, 16#00,
|
|
||||||
16#00, 16#00, 16#06, 16#73, 16#65, 16#72, 16#76, 16#65,
|
|
||||||
16#72, 16#00, 16#00, 16#00, 16#02, 16#74, 16#65, 16#00,
|
|
||||||
16#00, 16#00, 16#07, 16#74, 16#72, 16#61, 16#69, 16#6c,
|
|
||||||
16#65, 16#72, 16#00, 16#00, 16#00, 16#11, 16#74, 16#72,
|
|
||||||
16#61, 16#6e, 16#73, 16#66, 16#65, 16#72, 16#2d, 16#65,
|
|
||||||
16#6e, 16#63, 16#6f, 16#64, 16#69, 16#6e, 16#67, 16#00,
|
|
||||||
16#00, 16#00, 16#07, 16#75, 16#70, 16#67, 16#72, 16#61,
|
|
||||||
16#64, 16#65, 16#00, 16#00, 16#00, 16#0a, 16#75, 16#73,
|
|
||||||
16#65, 16#72, 16#2d, 16#61, 16#67, 16#65, 16#6e, 16#74,
|
|
||||||
16#00, 16#00, 16#00, 16#04, 16#76, 16#61, 16#72, 16#79,
|
|
||||||
16#00, 16#00, 16#00, 16#03, 16#76, 16#69, 16#61, 16#00,
|
|
||||||
16#00, 16#00, 16#07, 16#77, 16#61, 16#72, 16#6e, 16#69,
|
|
||||||
16#6e, 16#67, 16#00, 16#00, 16#00, 16#10, 16#77, 16#77,
|
|
||||||
16#77, 16#2d, 16#61, 16#75, 16#74, 16#68, 16#65, 16#6e,
|
|
||||||
16#74, 16#69, 16#63, 16#61, 16#74, 16#65, 16#00, 16#00,
|
|
||||||
16#00, 16#06, 16#6d, 16#65, 16#74, 16#68, 16#6f, 16#64,
|
|
||||||
16#00, 16#00, 16#00, 16#03, 16#67, 16#65, 16#74, 16#00,
|
|
||||||
16#00, 16#00, 16#06, 16#73, 16#74, 16#61, 16#74, 16#75,
|
|
||||||
16#73, 16#00, 16#00, 16#00, 16#06, 16#32, 16#30, 16#30,
|
|
||||||
16#20, 16#4f, 16#4b, 16#00, 16#00, 16#00, 16#07, 16#76,
|
|
||||||
16#65, 16#72, 16#73, 16#69, 16#6f, 16#6e, 16#00, 16#00,
|
|
||||||
16#00, 16#08, 16#48, 16#54, 16#54, 16#50, 16#2f, 16#31,
|
|
||||||
16#2e, 16#31, 16#00, 16#00, 16#00, 16#03, 16#75, 16#72,
|
|
||||||
16#6c, 16#00, 16#00, 16#00, 16#06, 16#70, 16#75, 16#62,
|
|
||||||
16#6c, 16#69, 16#63, 16#00, 16#00, 16#00, 16#0a, 16#73,
|
|
||||||
16#65, 16#74, 16#2d, 16#63, 16#6f, 16#6f, 16#6b, 16#69,
|
|
||||||
16#65, 16#00, 16#00, 16#00, 16#0a, 16#6b, 16#65, 16#65,
|
|
||||||
16#70, 16#2d, 16#61, 16#6c, 16#69, 16#76, 16#65, 16#00,
|
|
||||||
16#00, 16#00, 16#06, 16#6f, 16#72, 16#69, 16#67, 16#69,
|
|
||||||
16#6e, 16#31, 16#30, 16#30, 16#31, 16#30, 16#31, 16#32,
|
|
||||||
16#30, 16#31, 16#32, 16#30, 16#32, 16#32, 16#30, 16#35,
|
|
||||||
16#32, 16#30, 16#36, 16#33, 16#30, 16#30, 16#33, 16#30,
|
|
||||||
16#32, 16#33, 16#30, 16#33, 16#33, 16#30, 16#34, 16#33,
|
|
||||||
16#30, 16#35, 16#33, 16#30, 16#36, 16#33, 16#30, 16#37,
|
|
||||||
16#34, 16#30, 16#32, 16#34, 16#30, 16#35, 16#34, 16#30,
|
|
||||||
16#36, 16#34, 16#30, 16#37, 16#34, 16#30, 16#38, 16#34,
|
|
||||||
16#30, 16#39, 16#34, 16#31, 16#30, 16#34, 16#31, 16#31,
|
|
||||||
16#34, 16#31, 16#32, 16#34, 16#31, 16#33, 16#34, 16#31,
|
|
||||||
16#34, 16#34, 16#31, 16#35, 16#34, 16#31, 16#36, 16#34,
|
|
||||||
16#31, 16#37, 16#35, 16#30, 16#32, 16#35, 16#30, 16#34,
|
|
||||||
16#35, 16#30, 16#35, 16#32, 16#30, 16#33, 16#20, 16#4e,
|
|
||||||
16#6f, 16#6e, 16#2d, 16#41, 16#75, 16#74, 16#68, 16#6f,
|
|
||||||
16#72, 16#69, 16#74, 16#61, 16#74, 16#69, 16#76, 16#65,
|
|
||||||
16#20, 16#49, 16#6e, 16#66, 16#6f, 16#72, 16#6d, 16#61,
|
|
||||||
16#74, 16#69, 16#6f, 16#6e, 16#32, 16#30, 16#34, 16#20,
|
|
||||||
16#4e, 16#6f, 16#20, 16#43, 16#6f, 16#6e, 16#74, 16#65,
|
|
||||||
16#6e, 16#74, 16#33, 16#30, 16#31, 16#20, 16#4d, 16#6f,
|
|
||||||
16#76, 16#65, 16#64, 16#20, 16#50, 16#65, 16#72, 16#6d,
|
|
||||||
16#61, 16#6e, 16#65, 16#6e, 16#74, 16#6c, 16#79, 16#34,
|
|
||||||
16#30, 16#30, 16#20, 16#42, 16#61, 16#64, 16#20, 16#52,
|
|
||||||
16#65, 16#71, 16#75, 16#65, 16#73, 16#74, 16#34, 16#30,
|
|
||||||
16#31, 16#20, 16#55, 16#6e, 16#61, 16#75, 16#74, 16#68,
|
|
||||||
16#6f, 16#72, 16#69, 16#7a, 16#65, 16#64, 16#34, 16#30,
|
|
||||||
16#33, 16#20, 16#46, 16#6f, 16#72, 16#62, 16#69, 16#64,
|
|
||||||
16#64, 16#65, 16#6e, 16#34, 16#30, 16#34, 16#20, 16#4e,
|
|
||||||
16#6f, 16#74, 16#20, 16#46, 16#6f, 16#75, 16#6e, 16#64,
|
|
||||||
16#35, 16#30, 16#30, 16#20, 16#49, 16#6e, 16#74, 16#65,
|
|
||||||
16#72, 16#6e, 16#61, 16#6c, 16#20, 16#53, 16#65, 16#72,
|
|
||||||
16#76, 16#65, 16#72, 16#20, 16#45, 16#72, 16#72, 16#6f,
|
|
||||||
16#72, 16#35, 16#30, 16#31, 16#20, 16#4e, 16#6f, 16#74,
|
|
||||||
16#20, 16#49, 16#6d, 16#70, 16#6c, 16#65, 16#6d, 16#65,
|
|
||||||
16#6e, 16#74, 16#65, 16#64, 16#35, 16#30, 16#33, 16#20,
|
|
||||||
16#53, 16#65, 16#72, 16#76, 16#69, 16#63, 16#65, 16#20,
|
|
||||||
16#55, 16#6e, 16#61, 16#76, 16#61, 16#69, 16#6c, 16#61,
|
|
||||||
16#62, 16#6c, 16#65, 16#4a, 16#61, 16#6e, 16#20, 16#46,
|
|
||||||
16#65, 16#62, 16#20, 16#4d, 16#61, 16#72, 16#20, 16#41,
|
|
||||||
16#70, 16#72, 16#20, 16#4d, 16#61, 16#79, 16#20, 16#4a,
|
|
||||||
16#75, 16#6e, 16#20, 16#4a, 16#75, 16#6c, 16#20, 16#41,
|
|
||||||
16#75, 16#67, 16#20, 16#53, 16#65, 16#70, 16#74, 16#20,
|
|
||||||
16#4f, 16#63, 16#74, 16#20, 16#4e, 16#6f, 16#76, 16#20,
|
|
||||||
16#44, 16#65, 16#63, 16#20, 16#30, 16#30, 16#3a, 16#30,
|
|
||||||
16#30, 16#3a, 16#30, 16#30, 16#20, 16#4d, 16#6f, 16#6e,
|
|
||||||
16#2c, 16#20, 16#54, 16#75, 16#65, 16#2c, 16#20, 16#57,
|
|
||||||
16#65, 16#64, 16#2c, 16#20, 16#54, 16#68, 16#75, 16#2c,
|
|
||||||
16#20, 16#46, 16#72, 16#69, 16#2c, 16#20, 16#53, 16#61,
|
|
||||||
16#74, 16#2c, 16#20, 16#53, 16#75, 16#6e, 16#2c, 16#20,
|
|
||||||
16#47, 16#4d, 16#54, 16#63, 16#68, 16#75, 16#6e, 16#6b,
|
|
||||||
16#65, 16#64, 16#2c, 16#74, 16#65, 16#78, 16#74, 16#2f,
|
|
||||||
16#68, 16#74, 16#6d, 16#6c, 16#2c, 16#69, 16#6d, 16#61,
|
|
||||||
16#67, 16#65, 16#2f, 16#70, 16#6e, 16#67, 16#2c, 16#69,
|
|
||||||
16#6d, 16#61, 16#67, 16#65, 16#2f, 16#6a, 16#70, 16#67,
|
|
||||||
16#2c, 16#69, 16#6d, 16#61, 16#67, 16#65, 16#2f, 16#67,
|
|
||||||
16#69, 16#66, 16#2c, 16#61, 16#70, 16#70, 16#6c, 16#69,
|
|
||||||
16#63, 16#61, 16#74, 16#69, 16#6f, 16#6e, 16#2f, 16#78,
|
|
||||||
16#6d, 16#6c, 16#2c, 16#61, 16#70, 16#70, 16#6c, 16#69,
|
|
||||||
16#63, 16#61, 16#74, 16#69, 16#6f, 16#6e, 16#2f, 16#78,
|
|
||||||
16#68, 16#74, 16#6d, 16#6c, 16#2b, 16#78, 16#6d, 16#6c,
|
|
||||||
16#2c, 16#74, 16#65, 16#78, 16#74, 16#2f, 16#70, 16#6c,
|
|
||||||
16#61, 16#69, 16#6e, 16#2c, 16#74, 16#65, 16#78, 16#74,
|
|
||||||
16#2f, 16#6a, 16#61, 16#76, 16#61, 16#73, 16#63, 16#72,
|
|
||||||
16#69, 16#70, 16#74, 16#2c, 16#70, 16#75, 16#62, 16#6c,
|
|
||||||
16#69, 16#63, 16#70, 16#72, 16#69, 16#76, 16#61, 16#74,
|
|
||||||
16#65, 16#6d, 16#61, 16#78, 16#2d, 16#61, 16#67, 16#65,
|
|
||||||
16#3d, 16#67, 16#7a, 16#69, 16#70, 16#2c, 16#64, 16#65,
|
|
||||||
16#66, 16#6c, 16#61, 16#74, 16#65, 16#2c, 16#73, 16#64,
|
|
||||||
16#63, 16#68, 16#63, 16#68, 16#61, 16#72, 16#73, 16#65,
|
|
||||||
16#74, 16#3d, 16#75, 16#74, 16#66, 16#2d, 16#38, 16#63,
|
|
||||||
16#68, 16#61, 16#72, 16#73, 16#65, 16#74, 16#3d, 16#69,
|
|
||||||
16#73, 16#6f, 16#2d, 16#38, 16#38, 16#35, 16#39, 16#2d,
|
|
||||||
16#31, 16#2c, 16#75, 16#74, 16#66, 16#2d, 16#2c, 16#2a,
|
|
||||||
16#2c, 16#65, 16#6e, 16#71, 16#3d, 16#30, 16#2e >>).
|
|
|
@ -26,6 +26,8 @@
|
||||||
|
|
||||||
%% Tests.
|
%% Tests.
|
||||||
-export([check_status/1]).
|
-export([check_status/1]).
|
||||||
|
-export([echo_body/1]).
|
||||||
|
-export([echo_body_multi/1]).
|
||||||
|
|
||||||
%% ct.
|
%% ct.
|
||||||
|
|
||||||
|
@ -34,7 +36,9 @@ all() ->
|
||||||
|
|
||||||
groups() ->
|
groups() ->
|
||||||
[{spdy, [], [
|
[{spdy, [], [
|
||||||
check_status
|
check_status,
|
||||||
|
echo_body,
|
||||||
|
echo_body_multi
|
||||||
]}].
|
]}].
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
|
@ -82,6 +86,7 @@ init_dispatch(Config) ->
|
||||||
{"/static/[...]", cowboy_static,
|
{"/static/[...]", cowboy_static,
|
||||||
[{directory, ?config(static_dir, Config)},
|
[{directory, ?config(static_dir, Config)},
|
||||||
{mimetypes, [{<<".css">>, [<<"text/css">>]}]}]},
|
{mimetypes, [{<<".css">>, [<<"text/css">>]}]}]},
|
||||||
|
{"/echo/body", http_echo_body, []},
|
||||||
{"/chunked", http_chunked, []},
|
{"/chunked", http_chunked, []},
|
||||||
{"/", http_handler, []}
|
{"/", http_handler, []}
|
||||||
]}
|
]}
|
||||||
|
@ -120,3 +125,59 @@ check_status(Config) ->
|
||||||
{Ret, IsFin, Host, Path}
|
{Ret, IsFin, Host, Path}
|
||||||
end || {Status, Fin, Host, Path} <- Tests],
|
end || {Status, Fin, Host, Path} <- Tests],
|
||||||
gun:close(Pid).
|
gun:close(Pid).
|
||||||
|
|
||||||
|
echo_body(Config) ->
|
||||||
|
{_, Port} = lists:keyfind(port, 1, Config),
|
||||||
|
{ok, Pid} = gun:open("localhost", Port),
|
||||||
|
MRef = monitor(process, Pid),
|
||||||
|
Body = << 0:800000 >>,
|
||||||
|
StreamRef = gun:post(Pid, "/echo/body", [
|
||||||
|
{<<"content-length">>, integer_to_list(byte_size(Body))},
|
||||||
|
{<<"content-type">>, "application/octet-stream"}
|
||||||
|
], Body),
|
||||||
|
receive
|
||||||
|
{'DOWN', MRef, _, _, Reason} ->
|
||||||
|
error(Reason);
|
||||||
|
{gun_response, Pid, StreamRef, nofin, << "200", _/bits >>, _} ->
|
||||||
|
ok
|
||||||
|
after 1000 ->
|
||||||
|
error(response_timeout)
|
||||||
|
end,
|
||||||
|
receive
|
||||||
|
{'DOWN', MRef, _, _, Reason2} ->
|
||||||
|
error({gun_data, Reason2});
|
||||||
|
{gun_data, Pid, StreamRef, fin, Body} ->
|
||||||
|
ok
|
||||||
|
after 1000 ->
|
||||||
|
error(data_timeout)
|
||||||
|
end,
|
||||||
|
gun:close(Pid).
|
||||||
|
|
||||||
|
echo_body_multi(Config) ->
|
||||||
|
{_, Port} = lists:keyfind(port, 1, Config),
|
||||||
|
{ok, Pid} = gun:open("localhost", Port),
|
||||||
|
MRef = monitor(process, Pid),
|
||||||
|
BodyChunk = << 0:80000 >>,
|
||||||
|
StreamRef = gun:post(Pid, "/echo/body", [
|
||||||
|
{<<"content-length">>, integer_to_list(byte_size(BodyChunk) * 10)},
|
||||||
|
{<<"content-type">>, "application/octet-stream"}
|
||||||
|
]),
|
||||||
|
_ = [gun:data(Pid, StreamRef, nofin, BodyChunk) || _ <- lists:seq(1, 9)],
|
||||||
|
gun:data(Pid, StreamRef, fin, BodyChunk),
|
||||||
|
receive
|
||||||
|
{'DOWN', MRef, _, _, Reason} ->
|
||||||
|
error(Reason);
|
||||||
|
{gun_response, Pid, StreamRef, nofin, << "200", _/bits >>, _} ->
|
||||||
|
ok
|
||||||
|
after 1000 ->
|
||||||
|
error(response_timeout)
|
||||||
|
end,
|
||||||
|
receive
|
||||||
|
{'DOWN', MRef, _, _, Reason2} ->
|
||||||
|
error({gun_data, Reason2});
|
||||||
|
{gun_data, Pid, StreamRef, fin, << 0:800000 >>} ->
|
||||||
|
ok
|
||||||
|
after 1000 ->
|
||||||
|
error(data_timeout)
|
||||||
|
end,
|
||||||
|
gun:close(Pid).
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue