Browse Source

Completely remove SPDY

Loïc Hoguin 9 years ago
parent
commit
7bdd710849

+ 2 - 1
README.md

@@ -24,8 +24,9 @@ Sponsors
 The project is currently sponsored by
 [Sameroom](https://sameroom.io).
 
-The SPDY implementation was sponsored by
+The original SPDY implementation was sponsored by
 [LeoFS Cloud Storage](http://leo-project.net/leofs/).
+It has since been superseded by HTTP/2.
 
 Online documentation
 --------------------

+ 1 - 1
doc/src/guide/broken_clients.asciidoc

@@ -33,7 +33,7 @@ capitalize_hook(Status, Headers, Body, Req) ->
     cowboy_req:reply(Status, Headers2, Body, Req).
 ----
 
-Note that SPDY clients do not have that particular issue
+Note that HTTP/2 clients do not have that particular issue
 because the specification explicitly says all headers are
 lowercase, unlike HTTP which allows any case but treats
 them as case insensitive.

+ 1 - 1
doc/src/guide/erlang_web.asciidoc

@@ -106,7 +106,7 @@ new messages, and perform the operations required by only activating
 the required parts of the system.
 
 The more recent Web technologies, like Websocket of course, but also
-SPDY and HTTP/2.0, are all fully asynchronous protocols. The concept
+HTTP/2.0, are all fully asynchronous protocols. The concept
 of requests and responses is retained of course, but anything could
 be sent in between, by both the client or the browser, and the
 responses could also be received in a completely different order.

+ 1 - 1
doc/src/guide/introduction.asciidoc

@@ -4,7 +4,7 @@
 Cowboy is a small, fast and modular HTTP server written in Erlang.
 
 Cowboy aims to provide a complete HTTP stack, including its derivatives
-SPDY, Websocket and REST. Cowboy currently supports HTTP/1.0, HTTP/1.1,
+Websocket and REST. Cowboy currently supports HTTP/1.0, HTTP/1.1, HTTP/2,
 Websocket (all implemented drafts + standard) and Webmachine-based REST.
 
 Cowboy is a high quality project. It has a small code base, is very

+ 7 - 21
doc/src/guide/modern_web.asciidoc

@@ -180,35 +180,21 @@ A Websocket connection can be used to transfer any kind of data,
 small or big, text or binary. Because of this Websocket is
 sometimes used for communication between systems.
 
-=== SPDY
+=== HTTP/2
 
-SPDY is an attempt to reduce page loading time by opening a
+HTTP/2 is an attempt to reduce page loading time by opening a
 single connection per server, keeping it open for subsequent
 requests, and also by compressing the HTTP headers to reduce
 the size of requests.
 
-SPDY is compatible with HTTP/1.1 semantics, and is actually
+HTTP/2 is compatible with HTTP/1.1 semantics, and is actually
 just a different way of performing HTTP requests and responses,
 by using binary frames instead of a text-based protocol.
-SPDY also allows the server to send extra responses following
+HTTP/2 also allows the server to send extra responses following
 a request. This is meant to allow sending the resources
 associated with the request before the client requests them,
 saving latency when loading websites.
 
-SPDY is an experiment that has proven successful and is used
-as the basis for the HTTP/2.0 standard.
-
-Browsers make use of TLS Next Protocol Negotiation to upgrade
-to a SPDY connection seamlessly if the protocol supports it.
-
-The protocol itself has a few shortcomings which are being
-fixed in HTTP/2.0.
-
-=== HTTP/2.0
-
-HTTP/2.0 is the long-awaited update to the HTTP/1.1 protocol.
-It is based on SPDY although a lot has been improved at the
-time of writing.
-
-HTTP/2.0 is an asynchronous two-ways communication channel
-between two endpoints.
+Browsers make use of TLS Application-Layer Protocol Negotiation
+extension to upgrade to an HTTP/2 connection seamlessly if the
+server supports it.

+ 4 - 4
doc/src/guide/overview.asciidoc

@@ -55,7 +55,7 @@ HTTP/1.1 allows the client to request that the server
 keeps the connection alive. This mechanism is described
 in the next section.
 
-SPDY is designed to allow sending multiple requests
+HTTP/2 is designed to allow sending multiple requests
 asynchronously on the same connection. Details on what
 this means for your application is described in this
 chapter.
@@ -126,9 +126,9 @@ static files for example.
 
 This is handled automatically by the server.
 
-=== Asynchronous requests (SPDY)
+=== Asynchronous requests (HTTP/2)
 
-In SPDY, the client can send a request at any time.
+In HTTP/2, the client can send a request at any time.
 And the server can send a response at any time too.
 
 This means for example that the client does not need
@@ -142,7 +142,7 @@ Cowboy creates a new process for each request, and these
 processes are managed by another process that handles the
 connection itself.
 
-SPDY servers may also decide to send resources to the
+HTTP/2 servers may also decide to send resources to the
 client before the client requests them. This is especially
 useful for sending static files associated with the HTML
 page requested, as this reduces the latency of the overall

+ 0 - 10
doc/src/manual/cowboy.asciidoc

@@ -67,16 +67,6 @@ ProtoOpts = cowboy_protocol:opts():: HTTP protocol options.
 Start listening for HTTPS connections. Returns the pid for this
 listener's supervisor.
 
-=== start_spdy(Ref, NbAcceptors, TransOpts, ProtoOpts) -> {ok, pid()}
-
-Ref = ranch:ref():: Listener name.
-NbAcceptors = non_neg_integer():: Number of acceptor processes.
-TransOpts = ranch_ssl:opts():: SSL transport options.
-ProtoOpts = cowboy_spdy:opts():: SPDY protocol options.
-
-Start listening for SPDY connections. Returns the pid for this
-listener's supervisor.
-
 === stop_listener(Ref) -> ok | {error, not_found}
 
 Ref = ranch:ref():: Listener name.

+ 0 - 42
doc/src/manual/cowboy_spdy.asciidoc

@@ -1,42 +0,0 @@
-= cowboy_spdy(3)
-
-== Name
-
-cowboy_spdy - SPDY protocol
-
-== Description
-
-The `cowboy_spdy` module implements SPDY/3 as a Ranch protocol.
-
-== Types
-
-=== opts() = [Option]
-
-[source,erlang]
-----
-Option = {env, cowboy_middleware:env()}
-	| {middlewares, [module()]}
-	| {onresponse, cowboy:onresponse_fun()}
-----
-
-Configuration for the SPDY protocol handler.
-
-This configuration is passed to Cowboy when starting listeners
-using the `cowboy:start_spdy/4` function.
-
-It can be updated without restarting listeners using the
-Ranch functions `ranch:get_protocol_options/1` and
-`ranch:set_protocol_options/2`.
-
-== Option descriptions
-
-The default value is given next to the option name.
-
-env ([{listener, Ref}])::
-	Initial middleware environment.
-
-middlewares ([cowboy_router, cowboy_handler])::
-	List of middlewares to execute for every requests.
-
-onresponse (undefined)::
-	Fun called every time a response is sent.

+ 1 - 1
ebin/cowboy.app

@@ -1,7 +1,7 @@
 {application, cowboy, [
 	{description, "Small, fast, modular HTTP server."},
 	{vsn, "2.0.0-pre.2"},
-	{modules, ['cowboy','cowboy_app','cowboy_bstr','cowboy_clear','cowboy_clock','cowboy_constraints','cowboy_handler','cowboy_http','cowboy_http2','cowboy_loop','cowboy_middleware','cowboy_protocol','cowboy_req','cowboy_rest','cowboy_router','cowboy_spdy','cowboy_static','cowboy_stream','cowboy_stream_h','cowboy_sub_protocol','cowboy_sup','cowboy_tls','cowboy_websocket']},
+	{modules, ['cowboy','cowboy_app','cowboy_bstr','cowboy_clear','cowboy_clock','cowboy_constraints','cowboy_handler','cowboy_http','cowboy_http2','cowboy_loop','cowboy_middleware','cowboy_req','cowboy_rest','cowboy_router','cowboy_static','cowboy_stream','cowboy_stream_h','cowboy_sub_protocol','cowboy_sup','cowboy_tls','cowboy_websocket']},
 	{registered, [cowboy_sup,cowboy_clock]},
 	{applications, [kernel,stdlib,crypto,cowlib,ranch]},
 	{mod, {cowboy_app, []}}

+ 2 - 2
src/cowboy.erl

@@ -52,8 +52,8 @@ start_tls(Ref, NbAcceptors, TransOpts0, ProtoOpts)
 		when is_integer(NbAcceptors), NbAcceptors > 0 ->
 	TransOpts = [
 		connection_type(ProtoOpts),
-		{next_protocols_advertised, [<<"h2">>, <<"spdy/3">>, <<"http/1.1">>]},
-		{alpn_preferred_protocols, [<<"h2">>, <<"spdy/3">>, <<"http/1.1">>]}
+		{next_protocols_advertised, [<<"h2">>, <<"http/1.1">>]},
+		{alpn_preferred_protocols, [<<"h2">>, <<"http/1.1">>]}
 	|TransOpts0],
 	ranch:start_listener(Ref, NbAcceptors, ranch_ssl, TransOpts, cowboy_tls, ProtoOpts).
 

+ 0 - 25
src/cowboy_req.erl

@@ -769,11 +769,6 @@ response_headers(Headers, Req) ->
 %			%% We stream the response body until we close the connection.
 %			RespConn = close,
 %			{RespType, Req2} = if
-%				Transport =:= cowboy_spdy ->
-%					response(Status, Headers, RespHeaders, [
-%						{<<"date">>, cowboy_clock:rfc1123()},
-%						{<<"server">>, <<"Cowboy">>}
-%					], stream, Req);
 %				true ->
 %					response(Status, Headers, RespHeaders, [
 %						{<<"connection">>, <<"close">>},
@@ -896,9 +891,6 @@ chunk(Data, #{pid := Pid, streamid := StreamID}) ->
 
 %% If ever made public, need to send nothing if HEAD.
 -spec last_chunk(Req) -> Req when Req::req().
-last_chunk(Req=#http_req{socket=Socket, transport=cowboy_spdy}) ->
-	_ = cowboy_spdy:stream_close(Socket),
-	Req#http_req{resp_state=done};
 last_chunk(Req=#http_req{socket=Socket, transport=Transport}) ->
 	_ = Transport:send(Socket, <<"0\r\n\r\n">>),
 	Req#http_req{resp_state=done}.
@@ -1028,15 +1020,6 @@ to_list(Req) ->
 %-spec chunked_response(cowboy:http_status(), cowboy:http_headers(), Req) ->
 %	{normal | hook, Req} when Req::req().
 %chunked_response(Status, Headers, Req=#http_req{
-%		transport=cowboy_spdy, resp_state=waiting,
-%		resp_headers=RespHeaders}) ->
-%	{RespType, Req2} = response(Status, Headers, RespHeaders, [
-%		{<<"date">>, cowboy_clock:rfc1123()},
-%		{<<"server">>, <<"Cowboy">>}
-%	], stream, Req),
-%	{RespType, Req2#http_req{resp_state=chunks,
-%		resp_headers=[], resp_body= <<>>}};
-%chunked_response(Status, Headers, Req=#http_req{
 %		version=Version, connection=Connection,
 %		resp_state=RespState, resp_headers=RespHeaders})
 %		when RespState =:= waiting; RespState =:= waiting_stream ->
@@ -1094,14 +1077,6 @@ response(Status, Headers, RespHeaders, DefaultHeaders, Body, Req=#http_req{
 			end
 	end,
 	ReplyType = case Req2#http_req.resp_state of
-		waiting when Transport =:= cowboy_spdy, Body =:= stream ->
-			cowboy_spdy:stream_reply(Socket, status(Status2), FullHeaders2),
-			ReqPid ! {?MODULE, resp_sent},
-			normal;
-		waiting when Transport =:= cowboy_spdy ->
-			cowboy_spdy:reply(Socket, status(Status2), FullHeaders2, Body),
-			ReqPid ! {?MODULE, resp_sent},
-			normal;
 		RespState when RespState =:= waiting; RespState =:= waiting_stream ->
 			HTTPVer = atom_to_binary(Version, latin1),
 			StatusLine = << HTTPVer/binary, " ",

+ 0 - 488
src/cowboy_spdy.erl

@@ -1,488 +0,0 @@
-%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
-%%
-%% Permission to use, copy, modify, and/or distribute this software for any
-%% purpose with or without fee is hereby granted, provided that the above
-%% copyright notice and this permission notice appear in all copies.
-%%
-%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
--module(cowboy_spdy).
-
-%% API.
--export([start_link/4]).
-
-%% Internal.
--export([init/5]).
--export([system_continue/3]).
--export([system_terminate/4]).
--export([system_code_change/4]).
-
-%% Internal request process.
--export([request_init/10]).
--export([resume/5]).
--export([reply/4]).
--export([stream_reply/3]).
--export([stream_data/2]).
--export([stream_close/1]).
-
-%% Internal transport functions.
--export([name/0]).
--export([messages/0]).
--export([recv/3]).
--export([send/2]).
--export([sendfile/2]).
--export([setopts/2]).
-
--type streamid() :: non_neg_integer().
--type socket() :: {pid(), streamid()}.
-
--record(child, {
-	streamid :: streamid(),
-	pid :: pid(),
-	input = nofin :: fin | nofin,
-	in_buffer = <<>> :: binary(),
-	is_recv = false :: false | {active, socket(), pid()}
-		| {passive, socket(), pid(), non_neg_integer(), reference()},
-	output = nofin :: fin | nofin
-}).
-
--record(state, {
-	parent = undefined :: pid(),
-	socket :: inet:socket(),
-	transport :: module(),
-	buffer = <<>> :: binary(),
-	middlewares :: [module()],
-	env :: cowboy_middleware:env(),
-	onresponse :: cowboy:onresponse_fun(),
-	peer :: {inet:ip_address(), inet:port_number()},
-	zdef :: zlib:zstream(),
-	zinf :: zlib:zstream(),
-	last_streamid = 0 :: streamid(),
-	children = [] :: [#child{}]
-}).
-
--type opts() :: [{env, cowboy_middleware:env()}
-	| {middlewares, [module()]}
-	| {onresponse, cowboy:onresponse_fun()}].
--export_type([opts/0]).
-
-%% API.
-
--spec start_link(any(), inet:socket(), module(), any()) -> {ok, pid()}.
-start_link(Ref, Socket, Transport, Opts) ->
-	proc_lib:start_link(?MODULE, init,
-		[self(), Ref, Socket, Transport, Opts]).
-
-%% Internal.
-
-%% Faster alternative to proplists:get_value/3.
-get_value(Key, Opts, Default) ->
-	case lists:keyfind(Key, 1, Opts) of
-		{_, Value} -> Value;
-		_ -> Default
-	end.
-
--spec init(pid(), ranch:ref(), inet:socket(), module(), opts()) -> ok.
-init(Parent, Ref, Socket, Transport, Opts) ->
-	process_flag(trap_exit, true),
-	ok = proc_lib:init_ack(Parent, {ok, self()}),
-	{ok, Peer} = Transport:peername(Socket),
-	Middlewares = get_value(middlewares, Opts, [cowboy_router, cowboy_handler]),
-	Env = [{listener, Ref}|get_value(env, Opts, [])],
-	OnResponse = get_value(onresponse, Opts, undefined),
-	Zdef = cow_spdy:deflate_init(),
-	Zinf = cow_spdy:inflate_init(),
-	ok = ranch:accept_ack(Ref),
-	loop(#state{parent=Parent, socket=Socket, transport=Transport,
-		middlewares=Middlewares, env=Env,
-		onresponse=OnResponse, peer=Peer, zdef=Zdef, zinf=Zinf}).
-
-loop(State=#state{parent=Parent, socket=Socket, transport=Transport,
-		buffer=Buffer, children=Children}) ->
-	{OK, Closed, Error} = Transport:messages(),
-	Transport:setopts(Socket, [{active, once}]),
-	receive
-		{OK, Socket, Data} ->
-			parse_frame(State, << Buffer/binary, Data/binary >>);
-		{Closed, Socket} ->
-			terminate(State);
-		{Error, Socket, _Reason} ->
-			terminate(State);
-		{recv, FromSocket = {Pid, StreamID}, FromPid, Length, Timeout}
-				when Pid =:= self() ->
-			Child = #child{in_buffer=InBuffer, is_recv=false}
-				= get_child(StreamID, State),
-			if
-				Length =:= 0, InBuffer =/= <<>> ->
-					FromPid ! {recv, FromSocket, {ok, InBuffer}},
-					loop(replace_child(Child#child{in_buffer= <<>>}, State));
-				byte_size(InBuffer) >= Length ->
-					<< Data:Length/binary, Rest/bits >> = InBuffer,
-					FromPid ! {recv, FromSocket, {ok, Data}},
-					loop(replace_child(Child#child{in_buffer=Rest}, State));
-				true ->
-					TRef = erlang:send_after(Timeout, self(),
-						{recv_timeout, FromSocket}),
-					loop(replace_child(Child#child{
-						is_recv={passive, FromSocket, FromPid, Length, TRef}},
-						State))
-			end;
-		{recv_timeout, {Pid, StreamID}}
-				when Pid =:= self() ->
-			Child = #child{is_recv={passive, FromSocket, FromPid, _, _}}
-				= get_child(StreamID, State),
-			FromPid ! {recv, FromSocket, {error, timeout}},
-			loop(replace_child(Child, State));
-		{reply, {Pid, StreamID}, Status, Headers}
-				when Pid =:= self() ->
-			Child = #child{output=nofin} = get_child(StreamID, State),
-			syn_reply(State, StreamID, true, Status, Headers),
-			loop(replace_child(Child#child{output=fin}, State));
-		{reply, {Pid, StreamID}, Status, Headers, Body}
-				when Pid =:= self() ->
-			Child = #child{output=nofin} = get_child(StreamID, State),
-			syn_reply(State, StreamID, false, Status, Headers),
-			data(State, StreamID, true, Body),
-			loop(replace_child(Child#child{output=fin}, State));
-		{stream_reply, {Pid, StreamID}, Status, Headers}
-				when Pid =:= self() ->
-			#child{output=nofin} = get_child(StreamID, State),
-			syn_reply(State, StreamID, false, Status, Headers),
-			loop(State);
-		{stream_data, {Pid, StreamID}, Data}
-				when Pid =:= self() ->
-			#child{output=nofin} = get_child(StreamID, State),
-			data(State, StreamID, false, Data),
-			loop(State);
-		{stream_close, {Pid, StreamID}}
-				when Pid =:= self() ->
-			Child = #child{output=nofin} = get_child(StreamID, State),
-			data(State, StreamID, true, <<>>),
-			loop(replace_child(Child#child{output=fin}, State));
-		{sendfile, {Pid, StreamID}, Filepath}
-				when Pid =:= self() ->
-			Child = #child{output=nofin} = get_child(StreamID, State),
-			data_from_file(State, StreamID, Filepath),
-			loop(replace_child(Child#child{output=fin}, State));
-		{active, FromSocket = {Pid, StreamID}, FromPid} when Pid =:= self() ->
-			Child = #child{in_buffer=InBuffer, is_recv=false}
-				= get_child(StreamID, State),
-			case InBuffer of
-				<<>> ->
-					loop(replace_child(Child#child{
-						is_recv={active, FromSocket, FromPid}}, State));
-				_ ->
-					FromPid ! {spdy, FromSocket, InBuffer},
-					loop(replace_child(Child#child{in_buffer= <<>>}, State))
-			end;
-		{passive, FromSocket = {Pid, StreamID}, FromPid} when Pid =:= self() ->
-			Child = #child{is_recv=IsRecv} = get_child(StreamID, State),
-			%% Make sure we aren't in the middle of a recv call.
-			case IsRecv of false -> ok; {active, FromSocket, FromPid} -> ok end,
-			loop(replace_child(Child#child{is_recv=false}, State));
-		{'EXIT', Parent, Reason} ->
-			exit(Reason);
-		{'EXIT', Pid, _} ->
-			%% @todo Report the error if any.
-			loop(delete_child(Pid, State));
-		{system, From, Request} ->
-			sys:handle_system_msg(Request, From, Parent, ?MODULE, [], State);
-		%% Calls from the supervisor module.
-		{'$gen_call', {To, Tag}, which_children} ->
-			Workers = [{?MODULE, Pid, worker, [?MODULE]}
-				|| #child{pid=Pid} <- Children],
-			To ! {Tag, Workers},
-			loop(State);
-		{'$gen_call', {To, Tag}, count_children} ->
-			NbChildren = length(Children),
-			Counts = [{specs, 1}, {active, NbChildren},
-				{supervisors, 0}, {workers, NbChildren}],
-			To ! {Tag, Counts},
-			loop(State);
-		{'$gen_call', {To, Tag}, _} ->
-			To ! {Tag, {error, ?MODULE}},
-			loop(State)
-	after 60000 ->
-		goaway(State, ok),
-		terminate(State)
-	end.
-
--spec system_continue(_, _, #state{}) -> ok.
-system_continue(_, _, State) ->
-	loop(State).
-
--spec system_terminate(any(), _, _, _) -> no_return().
-system_terminate(Reason, _, _, _) ->
-	exit(Reason).
-
--spec system_code_change(Misc, _, _, _) -> {ok, Misc} when Misc::#state{}.
-system_code_change(Misc, _, _, _) ->
-	{ok, Misc}.
-
-parse_frame(State=#state{zinf=Zinf}, Data) ->
-	case cow_spdy:split(Data) of
-		{true, Frame, Rest} ->
-			P = cow_spdy:parse(Frame, Zinf),
-			case handle_frame(State#state{buffer = Rest}, P) of
-				error ->
-					terminate(State);
-				State2 ->
-					parse_frame(State2, Rest)
-			end;
-		false ->
-			loop(State#state{buffer=Data})
-	end.
-
-%% FLAG_UNIDIRECTIONAL can only be set by the server.
-handle_frame(State, {syn_stream, StreamID, _, _, true,
-		_, _, _, _, _, _, _}) ->
-	rst_stream(State, StreamID, protocol_error),
-	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),
-	State;
-%% SYN_STREAM.
-%%
-%% Erlang does not allow us to control the priority of processes
-%% so we ignore that value entirely.
-handle_frame(State=#state{middlewares=Middlewares, env=Env,
-		onresponse=OnResponse, peer=Peer},
-		{syn_stream, StreamID, _, IsFin, _, _,
-		Method, _, Host, Path, Version, Headers}) ->
-	Pid = spawn_link(?MODULE, request_init, [
-		{self(), StreamID}, Peer, OnResponse,
-		Env, Middlewares, Method, Host, Path, Version, Headers
-	]),
-	new_child(State, StreamID, Pid, IsFin);
-%% RST_STREAM.
-handle_frame(State, {rst_stream, StreamID, Status}) ->
-	error_logger:error_msg("Received RST_STREAM frame ~p ~p",
-		[StreamID, Status]),
-	%% @todo Stop StreamID.
-	State;
-%% PING initiated by the server; ignore, we don't send any.
-handle_frame(State, {ping, PingID}) when PingID rem 2 =:= 0 ->
-	error_logger:error_msg("Ignored PING control frame: ~p~n", [PingID]),
-	State;
-%% PING initiated by the client; send it back.
-handle_frame(State=#state{socket=Socket, transport=Transport},
-		{ping, PingID}) ->
-	Transport:send(Socket, cow_spdy:ping(PingID)),
-	State;
-%% Data received for a stream.
-handle_frame(State, {data, StreamID, IsFin, Data}) ->
-	Child = #child{input=nofin, in_buffer=Buffer, is_recv=IsRecv}
-		= get_child(StreamID, State),
-	Data2 = << Buffer/binary, Data/binary >>,
-	IsFin2 = if IsFin -> fin; true -> nofin end,
-	Child2 = case IsRecv of
-		{active, FromSocket, FromPid} ->
-			FromPid ! {spdy, FromSocket, Data},
-			Child#child{input=IsFin2, is_recv=false};
-		{passive, FromSocket, FromPid, 0, TRef} ->
-			FromPid ! {recv, FromSocket, {ok, Data2}},
-			cancel_recv_timeout(StreamID, TRef),
-			Child#child{input=IsFin2, in_buffer= <<>>, is_recv=false};
-		{passive, FromSocket, FromPid, Length, TRef}
-				when byte_size(Data2) >= Length ->
-			<< Data3:Length/binary, Rest/bits >> = Data2,
-			FromPid ! {recv, FromSocket, {ok, Data3}},
-			cancel_recv_timeout(StreamID, TRef),
-			Child#child{input=IsFin2, in_buffer=Rest, is_recv=false};
-		_ ->
-			Child#child{input=IsFin2, in_buffer=Data2}
-	end,
-	replace_child(Child2, State);
-%% General error, can't recover.
-handle_frame(State, {error, badprotocol}) ->
-	goaway(State, protocol_error),
-	error;
-%% Ignore all other frames for now.
-handle_frame(State, Frame) ->
-	error_logger:error_msg("Ignored frame ~p", [Frame]),
-	State.
-
-cancel_recv_timeout(StreamID, TRef) ->
-	_ = erlang:cancel_timer(TRef),
-	receive
-		{recv_timeout, {Pid, StreamID}}
-				when Pid =:= self() ->
-			ok
-	after 0 ->
-		ok
-	end.
-
-%% @todo We must wait for the children to finish here,
-%% but only up to N milliseconds. Then we shutdown.
-terminate(_State) ->
-	ok.
-
-syn_reply(#state{socket=Socket, transport=Transport, zdef=Zdef},
-		StreamID, IsFin, Status, Headers) ->
-	Transport:send(Socket, cow_spdy:syn_reply(Zdef, StreamID, IsFin,
-		Status, <<"HTTP/1.1">>, Headers)).
-
-rst_stream(#state{socket=Socket, transport=Transport}, StreamID, Status) ->
-	Transport:send(Socket, cow_spdy:rst_stream(StreamID, Status)).
-
-goaway(#state{socket=Socket, transport=Transport, last_streamid=LastStreamID},
-		Status) ->
-	Transport:send(Socket, cow_spdy:goaway(LastStreamID, Status)).
-
-data(#state{socket=Socket, transport=Transport}, StreamID, IsFin, Data) ->
-	Transport:send(Socket, cow_spdy:data(StreamID, IsFin, Data)).
-
-data_from_file(#state{socket=Socket, transport=Transport},
-		StreamID, Filepath) ->
-	{ok, IoDevice} = file:open(Filepath, [read, binary, raw]),
-	data_from_file(Socket, Transport, StreamID, IoDevice).
-
-data_from_file(Socket, Transport, StreamID, IoDevice) ->
-	case file:read(IoDevice, 16#1fff) of
-		eof ->
-			_ = Transport:send(Socket, cow_spdy:data(StreamID, true, <<>>)),
-			ok;
-		{ok, Data} ->
-			case Transport:send(Socket, cow_spdy:data(StreamID, false, Data)) of
-				ok ->
-					data_from_file(Socket, Transport, StreamID, IoDevice);
-				{error, _} ->
-					ok
-			end
-	end.
-
-%% Children.
-
-new_child(State=#state{children=Children}, StreamID, Pid, IsFin) ->
-	IsFin2 = if IsFin -> fin; true -> nofin end,
-	State#state{last_streamid=StreamID,
-		children=[#child{streamid=StreamID,
-		pid=Pid, input=IsFin2}|Children]}.
-
-get_child(StreamID, #state{children=Children}) ->
-	lists:keyfind(StreamID, #child.streamid, Children).
-
-replace_child(Child=#child{streamid=StreamID},
-		State=#state{children=Children}) ->
-	Children2 = lists:keyreplace(StreamID, #child.streamid, Children, Child),
-	State#state{children=Children2}.
-
-delete_child(Pid, State=#state{children=Children}) ->
-	Children2 = lists:keydelete(Pid, #child.pid, Children),
-	State#state{children=Children2}.
-
-%% Request process.
-
--spec request_init(socket(), {inet:ip_address(), inet:port_number()},
-		cowboy:onresponse_fun(), cowboy_middleware:env(), [module()],
-		binary(), binary(), binary(), binary(), [{binary(), binary()}])
-	-> ok.
-request_init(FakeSocket, Peer, OnResponse,
-		Env, Middlewares, Method, Host, Path, Version, Headers) ->
-	{Host2, Port} = cow_http_hd:parse_host(Host),
-	{Path2, Qs} = cow_http:parse_fullpath(Path),
-	Version2 = cow_http:parse_version(Version),
-	Req = cowboy_req:new(FakeSocket, ?MODULE, Peer,
-		Method, Path2, Qs, Version2, Headers,
-		Host2, Port, <<>>, true, false, OnResponse),
-	execute(Req, Env, Middlewares).
-
--spec execute(cowboy_req:req(), cowboy_middleware:env(), [module()])
-	-> ok.
-execute(Req, _, []) ->
-	cowboy_req:ensure_response(Req, 204);
-execute(Req, Env, [Middleware|Tail]) ->
-	case Middleware:execute(Req, Env) of
-		{ok, Req2, Env2} ->
-			execute(Req2, Env2, Tail);
-		{suspend, Module, Function, Args} ->
-			erlang:hibernate(?MODULE, resume,
-				[Env, Tail, Module, Function, Args]);
-		{stop, Req2} ->
-			cowboy_req:ensure_response(Req2, 204)
-	end.
-
--spec resume(cowboy_middleware:env(), [module()],
-	module(), module(), [any()]) -> ok.
-resume(Env, Tail, Module, Function, Args) ->
-	case apply(Module, Function, Args) of
-		{ok, Req2, Env2} ->
-			execute(Req2, Env2, Tail);
-		{suspend, Module2, Function2, Args2} ->
-			erlang:hibernate(?MODULE, resume,
-				[Env, Tail, Module2, Function2, Args2]);
-		{stop, Req2} ->
-			cowboy_req:ensure_response(Req2, 204)
-	end.
-
-%% Reply functions used by cowboy_req.
-
--spec reply(socket(), binary(), cowboy:http_headers(), iodata()) -> ok.
-reply(Socket = {Pid, _}, Status, Headers, Body) ->
-	_ = case iolist_size(Body) of
-		0 -> Pid ! {reply, Socket, Status, Headers};
-		_ -> Pid ! {reply, Socket, Status, Headers, Body}
-	end,
-	ok.
-
--spec stream_reply(socket(), binary(), cowboy:http_headers()) -> ok.
-stream_reply(Socket = {Pid, _}, Status, Headers) ->
-	_ = Pid ! {stream_reply, Socket, Status, Headers},
-	ok.
-
--spec stream_data(socket(), iodata()) -> ok.
-stream_data(Socket = {Pid, _}, Data) ->
-	_ = Pid ! {stream_data, Socket, Data},
-	ok.
-
--spec stream_close(socket()) -> ok.
-stream_close(Socket = {Pid, _}) ->
-	_ = Pid ! {stream_close, Socket},
-	ok.
-
-%% Internal transport functions.
-
--spec name() -> spdy.
-name() ->
-	spdy.
-
--spec messages() -> {spdy, spdy_closed, spdy_error}.
-messages() ->
-	{spdy, spdy_closed, spdy_error}.
-
--spec recv(socket(), non_neg_integer(), timeout())
-	-> {ok, binary()} | {error, timeout}.
-recv(Socket = {Pid, _}, Length, Timeout) ->
-	_ = Pid ! {recv, Socket, self(), Length, Timeout},
-	receive
-		{recv, Socket, Ret} ->
-			Ret
-	end.
-
--spec send(socket(), iodata()) -> ok.
-send(Socket, Data) ->
-	stream_data(Socket, Data).
-
-%% We don't wait for the result of the actual sendfile call,
-%% therefore we can't know how much was actually sent.
-%% This isn't a problem as we don't use this value in Cowboy.
--spec sendfile(socket(), file:name_all()) -> {ok, undefined}.
-sendfile(Socket = {Pid, _}, Filepath) ->
-	_ = Pid ! {sendfile, Socket, Filepath},
-	{ok, undefined}.
-
--spec setopts({pid(), _}, list()) -> ok.
-setopts(Socket = {Pid, _}, [{active, once}]) ->
-	_ = Pid ! {active, Socket, self()},
-	ok;
-setopts(Socket = {Pid, _}, [{active, false}]) ->
-	_ = Pid ! {passive, Socket, self()},
-	ok.

+ 0 - 3
src/cowboy_tls.erl

@@ -29,9 +29,6 @@ init(Parent, Ref, Socket, Transport, Opts) ->
 	case ssl:negotiated_protocol(Socket) of
 		{ok, <<"h2">>} ->
 			init(Parent, Ref, Socket, Transport, Opts, cowboy_http2);
-		%% @todo Implement cowboy_spdy and cowboy_http.
-		{ok, <<"spdy/3">>} ->
-			init(Parent, Ref, Socket, Transport, Opts, cowboy_spdy);
 		_ -> %% http/1.1 or no protocol negotiated.
 			init(Parent, Ref, Socket, Transport, Opts, cowboy_http)
 	end.

+ 2 - 19
test/cowboy_test.erl

@@ -30,40 +30,28 @@ init_https(Ref, ProtoOpts, Config) ->
 	Port = ranch:get_port(Ref),
 	[{type, ssl}, {protocol, http}, {port, Port}, {opts, Opts}|Config].
 
-init_spdy(Ref, ProtoOpts, Config) ->
-	Opts = ct_helper:get_certs_from_ets(),
-	{ok, _} = cowboy:start_tls(Ref, 100, Opts ++ [{port, 0}], ProtoOpts),
-	Port = ranch:get_port(Ref),
-	[{type, ssl}, {protocol, spdy}, {port, Port}, {opts, Opts}|Config].
-
 %% Common group of listeners used by most suites.
 
 common_all() ->
 	[
 		{group, http},
 		{group, https},
-		{group, spdy},
 		{group, http_compress},
-		{group, https_compress},
-		{group, spdy_compress}
+		{group, https_compress}
 	].
 
 common_groups(Tests) ->
 	[
 		{http, [parallel], Tests},
 		{https, [parallel], Tests},
-		{spdy, [parallel], Tests},
 		{http_compress, [parallel], Tests},
-		{https_compress, [parallel], Tests},
-		{spdy_compress, [parallel], Tests}
+		{https_compress, [parallel], Tests}
 	].
 
 init_common_groups(Name = http, Config, Mod) ->
 	init_http(Name, #{env => #{dispatch => Mod:init_dispatch(Config)}}, Config);
 init_common_groups(Name = https, Config, Mod) ->
 	init_https(Name, #{env => #{dispatch => Mod:init_dispatch(Config)}}, Config);
-init_common_groups(Name = spdy, Config, Mod) ->
-	init_https(Name, #{env => #{dispatch => Mod:init_dispatch(Config)}}, Config);
 init_common_groups(Name = http_compress, Config, Mod) ->
 	init_http(Name, #{
 		env => #{dispatch => Mod:init_dispatch(Config)},
@@ -73,11 +61,6 @@ init_common_groups(Name = https_compress, Config, Mod) ->
 	init_https(Name, #{
 		env => #{dispatch => Mod:init_dispatch(Config)},
 		compress => true
-	}, Config);
-init_common_groups(Name = spdy_compress, Config, Mod) ->
-	init_spdy(Name, #{
-		env => #{dispatch => Mod:init_dispatch(Config)},
-		compress => true
 	}, Config).
 
 %% Support functions for testing using Gun.

+ 0 - 147
test/spdy_SUITE.erl

@@ -1,147 +0,0 @@
-%% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
-%%
-%% Permission to use, copy, modify, and/or distribute this software for any
-%% purpose with or without fee is hereby granted, provided that the above
-%% copyright notice and this permission notice appear in all copies.
-%%
-%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
--module(spdy_SUITE).
--compile(export_all).
-
--import(ct_helper, [config/2]).
--import(cowboy_test, [gun_open/1]).
--import(cowboy_test, [raw_open/1]).
--import(cowboy_test, [raw_send/2]).
-
-%% ct.
-
-all() ->
-	[{group, spdy}].
-
-groups() ->
-	[{spdy, [], ct_helper:all(?MODULE)}].
-
-init_per_suite(Config) ->
-	case proplists:get_value(ssl_app, ssl:versions()) of
-		Version when Version < "5.2.1" ->
-			{skip, "No NPN support in SSL application."};
-		_ ->
-			Dir = config(priv_dir, Config) ++ "/static",
-			ct_helper:create_static_dir(Dir),
-			[{static_dir, Dir}|Config]
-	end.
-
-end_per_suite(Config) ->
-	ct_helper:delete_static_dir(config(static_dir, Config)).
-
-init_per_group(Name, Config) ->
-	cowboy_test:init_spdy(Name, [
-		{env, [{dispatch, init_dispatch(Config)}]}
-	], Config).
-
-end_per_group(Name, _) ->
-	cowboy:stop_listener(Name).
-
-%% Dispatch configuration.
-
-init_dispatch(Config) ->
-	cowboy_router:compile([
-		{"localhost", [
-			{"/static/[...]", cowboy_static,
-				{dir, config(static_dir, Config)}},
-			{"/echo/body", http_echo_body, []},
-			{"/chunked", http_chunked, []},
-			{"/", http_handler, []}
-		]}
-	]).
-
-%% Convenience functions.
-
-do_get(ConnPid, Host, Path) ->
-	StreamRef = gun:get(ConnPid, Path, [{<<"host">>, Host}]),
-	{response, IsFin, Status, _} = gun:await(ConnPid, StreamRef),
-	{IsFin, Status}.
-
-%% Tests.
-
-check_status(Config) ->
-	Tests = [
-		{200, nofin, "localhost", "/"},
-		{200, nofin, "localhost", "/chunked"},
-		{200, nofin, "localhost", "/static/style.css"},
-		{400, fin, "bad-host", "/"},
-		{400, fin, "localhost", "bad-path"},
-		{404, fin, "localhost", "/this/path/does/not/exist"}
-	],
-	ConnPid = gun_open(Config),
-	_ = [{Status, Fin, Host, Path} = begin
-		{IsFin, Ret} = do_get(ConnPid, Host, Path),
-		{Ret, IsFin, Host, Path}
-	end || {Status, Fin, Host, Path} <- Tests],
-	gun:close(ConnPid).
-
-echo_body(Config) ->
-	ConnPid = gun_open(Config),
-	Body = << 0:800000 >>,
-	StreamRef = gun:post(ConnPid, "/echo/body", [
-		{<<"content-type">>, "application/octet-stream"}
-	], Body),
-	{response, nofin, 200, _} = gun:await(ConnPid, StreamRef),
-	{ok, Body} = gun:await_body(ConnPid, StreamRef),
-	gun:close(ConnPid).
-
-echo_body_multi(Config) ->
-	ConnPid = gun_open(Config),
-	BodyChunk = << 0:80000 >>,
-	StreamRef = gun:post(ConnPid, "/echo/body", [
-		%% @todo I'm still unhappy with this. It shouldn't be required...
-		{<<"content-length">>, integer_to_list(byte_size(BodyChunk) * 10)},
-		{<<"content-type">>, "application/octet-stream"}
-	]),
-	_ = [gun:data(ConnPid, StreamRef, nofin, BodyChunk) || _ <- lists:seq(1, 9)],
-	gun:data(ConnPid, StreamRef, fin, BodyChunk),
-	{response, nofin, 200, _} = gun:await(ConnPid, StreamRef),
-	{ok, << 0:800000 >>} = gun:await_body(ConnPid, StreamRef),
-	gun:close(ConnPid).
-
-two_frames_one_packet(Config) ->
-	{raw_client, Socket, Transport} = Client = raw_open([
-		{opts, [{client_preferred_next_protocols,
-			{client, [<<"spdy/3">>], <<"spdy/3">>}}]}
-		|Config]),
-	Zdef = cow_spdy:deflate_init(),
-	Zinf = cow_spdy:inflate_init(),
-	ok = raw_send(Client, iolist_to_binary([
-		cow_spdy:syn_stream(Zdef, 1, 0, true, false,
-			0, <<"GET">>, <<"https">>, <<"localhost">>,
-			<<"/">>, <<"HTTP/1.1">>, []),
-		cow_spdy:syn_stream(Zdef, 3, 0, true, false,
-			0, <<"GET">>, <<"https">>, <<"localhost">>,
-			<<"/">>, <<"HTTP/1.1">>, [])
-	])),
-	{Frame1, Rest1} = spdy_recv(Socket, Transport, <<>>),
-	{syn_reply, _, false, <<"200 OK">>, _, _} = cow_spdy:parse(Frame1, Zinf),
-	{Frame2, Rest2} = spdy_recv(Socket, Transport, Rest1),
-	{data, 1, true, _} = cow_spdy:parse(Frame2, Zinf),
-	{Frame3, Rest3} = spdy_recv(Socket, Transport, Rest2),
-	{syn_reply, _, false, <<"200 OK">>, _, _} = cow_spdy:parse(Frame3, Zinf),
-	{Frame4, <<>>} = spdy_recv(Socket, Transport, Rest3),
-	{data, 3, true, _} = cow_spdy:parse(Frame4, Zinf),
-	ok.
-
-spdy_recv(Socket, Transport, Acc) ->
-	{ok, Data} = Transport:recv(Socket, 0, 5000),
-	Data2 = << Acc/binary, Data/bits >>,
-	case cow_spdy:split(Data2) of
-		false ->
-			spdy_recv(Socket, Transport, Data2);
-		{true, Frame, Rest} ->
-			{Frame, Rest}
-	end.