Browse Source

Add sendfile support to SPDY, enabling cowboy_static use

Loïc Hoguin 12 years ago
parent
commit
1fc69977da
2 changed files with 48 additions and 4 deletions
  1. 37 1
      src/cowboy_spdy.erl
  2. 11 3
      test/spdy_SUITE.erl

+ 37 - 1
src/cowboy_spdy.erl

@@ -42,6 +42,7 @@
 %% Internal transport functions.
 %% Internal transport functions.
 -export([name/0]).
 -export([name/0]).
 -export([send/2]).
 -export([send/2]).
+-export([sendfile/2]).
 
 
 -record(child, {
 -record(child, {
 	streamid :: non_neg_integer(),
 	streamid :: non_neg_integer(),
@@ -174,6 +175,14 @@ loop(State=#state{parent=Parent, socket=Socket, transport=Transport,
 			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});
+		{sendfile, {Pid, StreamID}, Filepath}
+				when Pid =:= self() ->
+			Child = #child{output=nofin} = lists:keyfind(StreamID,
+				#child.streamid, Children),
+			data_from_file(State, StreamID, Filepath),
+			Children2 = lists:keyreplace(StreamID,
+				#child.streamid, Children, Child#child{output=fin}),
+			loop(State#state{children=Children2});
 		{'EXIT', Parent, Reason} ->
 		{'EXIT', Parent, Reason} ->
 			exit(Reason);
 			exit(Reason);
 		{'EXIT', Pid, _} ->
 		{'EXIT', Pid, _} ->
@@ -430,6 +439,27 @@ data(#state{socket=Socket, transport=Transport}, IsFin, StreamID, Data) ->
 		<< 0:1, StreamID:31, Flags:8, Len:24 >>,
 		<< 0:1, StreamID:31, Flags:8, Len:24 >>,
 		Data]).
 		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, << 0:1, StreamID:31, 1:8, 0:24 >>),
+			ok;
+		{ok, Data} ->
+			Len = byte_size(Data),
+			Data2 = [<< 0:1, StreamID:31, 0:8, Len:24 >>, Data],
+			case Transport:send(Socket, Data2) of
+				ok ->
+					data_from_file(Socket, Transport, StreamID, IoDevice);
+				{error, _} ->
+					ok
+			end
+	end.
+
 %% Request process.
 %% Request process.
 
 
 request_init(Parent, StreamID, Peer,
 request_init(Parent, StreamID, Peer,
@@ -535,10 +565,16 @@ stream_close(Socket = {Pid, _}) ->
 	ok.
 	ok.
 
 
 %% Internal transport functions.
 %% Internal transport functions.
-%% @todo recv, sendfile
+%% @todo recv
 
 
 name() ->
 name() ->
 	spdy.
 	spdy.
 
 
 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,
+%% therefore we can't know how much was actually sent.
+sendfile(Socket = {Pid, _}, Filepath) ->
+	_ = Pid ! {sendfile, Socket, Filepath},
+	{ok, undefined}.

+ 11 - 3
test/spdy_SUITE.erl

@@ -44,9 +44,13 @@ init_per_suite(Config) ->
 	application:start(cowboy),
 	application:start(cowboy),
 	application:start(public_key),
 	application:start(public_key),
 	application:start(ssl),
 	application:start(ssl),
-	Config.
+	Dir = ?config(priv_dir, Config) ++ "/static",
+	ct_helper:create_static_dir(Dir),
+	[{static_dir, Dir}|Config].
 
 
-end_per_suite(_Config) ->
+end_per_suite(Config) ->
+	Dir = ?config(static_dir, Config),
+	ct_helper:delete_static_dir(Dir),
 	application:stop(ssl),
 	application:stop(ssl),
 	application:stop(public_key),
 	application:stop(public_key),
 	application:stop(cowboy),
 	application:stop(cowboy),
@@ -69,9 +73,12 @@ end_per_group(Name, _) ->
 
 
 %% Dispatch configuration.
 %% Dispatch configuration.
 
 
-init_dispatch(_) ->
+init_dispatch(Config) ->
 	cowboy_router:compile([
 	cowboy_router:compile([
 		{"localhost", [
 		{"localhost", [
+			{"/static/[...]", cowboy_static,
+				[{directory, ?config(static_dir, Config)},
+				 {mimetypes, [{<<".css">>, [<<"text/css">>]}]}]},
 			{"/chunked", http_chunked, []},
 			{"/chunked", http_chunked, []},
 			{"/", http_handler, []}
 			{"/", http_handler, []}
 		]}
 		]}
@@ -152,6 +159,7 @@ check_status(Config) ->
 	Tests = [
 	Tests = [
 		{200, nofin, "localhost", "/"},
 		{200, nofin, "localhost", "/"},
 		{200, nofin, "localhost", "/chunked"},
 		{200, nofin, "localhost", "/chunked"},
+		{200, nofin, "localhost", "/static/style.css"},
 		{400, fin, "bad-host", "/"},
 		{400, fin, "bad-host", "/"},
 		{400, fin, "localhost", "bad-path"},
 		{400, fin, "localhost", "bad-path"},
 		{404, fin, "localhost", "/this/path/does/not/exist"}
 		{404, fin, "localhost", "/this/path/does/not/exist"}