Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 251 lines (219 sloc) 6.902 kb
cd2deb9 @vinoski Add sendfile linked-in driver for use on Linux and OS X. It is not yet t...
vinoski authored
1 %%% File : yaws_sendfile.erl
2 %%% Author : Steve Vinoski <vinoski@ieee.org>
3 %%% Description : interface to sendfile linked-in driver for Yaws
4 %%% Created : 9 Nov 2008 by Steve Vinoski <vinoski@ieee.org>
5
6 -module(yaws_sendfile).
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
7 -author('vinoski@ieee.org').
8
9 -export([start_link/0, start/0, stop/0,
10 enabled/0, send/2, send/3, send/4]).
11
12 -include("yaws_configure.hrl").
13 -include("../include/yaws.hrl").
2150708 @tuncer Use file:sendfile/5 if available
tuncer authored
14 -include_lib("kernel/include/file.hrl").
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
15
2150708 @tuncer Use file:sendfile/5 if available
tuncer authored
16 -ifndef(HAVE_YAWS_SENDFILE).
17 -ifndef(NO_FILE_SENDFILE).
18 -define(HAVE_FILE_SENDFILE, 1).
19 -endif.
20 -endif.
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
21
2150708 @tuncer Use file:sendfile/5 if available
tuncer authored
22
23 -ifdef(HAVE_YAWS_SENDFILE).
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
24 -behavior(gen_server).
25 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
26 code_change/3]).
4aa146b @vinoski fix dialyzer problems in yaws_sendfile changes
vinoski authored
27 -endif.
28
29 send(Out, Filename) ->
30 send(Out, Filename, 0, all).
31 send(Out, Filename, Offset) ->
32 send(Out, Filename, Offset, all).
33
2150708 @tuncer Use file:sendfile/5 if available
tuncer authored
34 bytes_to_transfer(Filename, Offset, Count) ->
35 case Count of
36 all ->
37 case file:read_file_info(Filename) of
38 {ok, #file_info{size = Size}} ->
39 Size - Offset;
40 Error ->
41 Error
42 end;
43 Count when is_integer(Count) ->
44 Count;
45 _ ->
46 {error, badarg}
47 end.
48
ddfa257 @vinoski rebar: use file:sendfile/5 if erts version > R15B
vinoski authored
49 -ifdef(HAVE_FILE_SENDFILE). %% OTP > R15B; use file:sendfile/5
2150708 @tuncer Use file:sendfile/5 if available
tuncer authored
50
51 enabled() ->
52 true.
53 send(Out, Filename, Offset, Count) ->
54 Count1 = bytes_to_transfer(Filename, Offset, Count),
55 case Count1 of
56 {error, _}=Error1 ->
57 Error1;
58 _ ->
59 case file:open(Filename, [raw, read, binary]) of
60 {ok, RawFile} ->
61 Res = file:sendfile(RawFile, Out, Offset, Count1, []),
62 ok = file:close(RawFile),
63 Res;
64 Error2 ->
65 Error2
66 end
67 end.
68 start_link() ->
69 ignore.
70 start() ->
71 ignore.
72 stop() ->
73 ok.
74
75 -else.
76
77 -ifdef(HAVE_YAWS_SENDFILE).
4aa146b @vinoski fix dialyzer problems in yaws_sendfile changes
vinoski authored
78
12471ad @klacke added configure support for sendfile
authored
79 start_link() ->
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
80 gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
cd2deb9 @vinoski Add sendfile linked-in driver for use on Linux and OS X. It is not yet t...
vinoski authored
81
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
82 start() ->
83 gen_server:start({local, ?MODULE}, ?MODULE, [], []).
cd2deb9 @vinoski Add sendfile linked-in driver for use on Linux and OS X. It is not yet t...
vinoski authored
84
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
85 enabled() ->
86 true.
cd2deb9 @vinoski Add sendfile linked-in driver for use on Linux and OS X. It is not yet t...
vinoski authored
87
88 send(Out, Filename, Offset, Count) ->
2150708 @tuncer Use file:sendfile/5 if available
tuncer authored
89 Count1 = bytes_to_transfer(Filename, Offset, Count),
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
90 case Count1 of
2150708 @tuncer Use file:sendfile/5 if available
tuncer authored
91 {error, _}=Error ->
92 Error;
cd2deb9 @vinoski Add sendfile linked-in driver for use on Linux and OS X. It is not yet t...
vinoski authored
93 _ ->
94 case prim_inet:getfd(Out) of
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
95 {ok, SocketFd} ->
d75acaf @vinoski restore overflow handling dropped in previous commit
vinoski authored
96 do_send(Out, SocketFd, Filename, Offset, Count1);
2150708 @tuncer Use file:sendfile/5 if available
tuncer authored
97 Error2 ->
98 Error2
cd2deb9 @vinoski Add sendfile linked-in driver for use on Linux and OS X. It is not yet t...
vinoski authored
99 end
100 end.
101
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
102 stop() ->
103 gen_server:cast(?MODULE, stop).
104
105 -record(state, {
106 port, % driver port
107 caller_tbl % table mapping socket fd to caller
108 }).
109
110 init([]) ->
111 process_flag(trap_exit, true),
112 Shlib = "yaws_sendfile_drv",
113 Dir = case yaws_generated:is_local_install() of
114 true ->
115 filename:dirname(code:which(?MODULE)) ++ "/../priv/lib";
116 false ->
117 %% ignore dialyzer on this one
118 PrivDir = code:priv_dir(yaws),
119 filename:join(PrivDir,"lib")
120 end,
121 case erl_ddll:load_driver(Dir, Shlib) of
122 ok -> ok;
123 {error, already_loaded} -> ok;
b7b8b7d @tuncer fix missing whitespace in yaws_sendfile error message
tuncer authored
124 _ -> exit({error, "could not load driver " ++ Shlib})
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
125 end,
126 Port = open_port({spawn, Shlib}, [binary]),
127 CallerTable = ets:new(yaws_sendfile, []),
128 {ok, #state{port = Port, caller_tbl = CallerTable}}.
129
130 handle_call({send, SocketFd, Msg}, From, State) ->
131 true = erlang:port_command(State#state.port, Msg),
132 true = ets:insert(State#state.caller_tbl, {SocketFd, From}),
133 {noreply, State};
134 handle_call(_Req, _From, State) ->
135 {reply, ok, State}.
136
137 handle_info({_, {data, <<Cnt:64, SocketFd:32, Res:8, Err/binary>>}}, State) ->
138 Reply = case Res of
139 1 ->
140 {ok, Cnt};
141 0 ->
142 {error,
143 list_to_atom(
144 lists:takewhile(fun(El) -> El =/= 0 end,
145 binary_to_list(Err)))}
146 end,
147 CallerTable = State#state.caller_tbl,
148 [{SocketFd, From}] = ets:lookup(CallerTable, SocketFd),
149 gen_server:reply(From, Reply),
150 ets:delete(CallerTable, SocketFd),
151 {noreply, State};
152 handle_info(_Info, State) ->
153 {noreply, State}.
154
155 handle_cast(stop, State) ->
156 {stop, State};
157 handle_cast(_, State) ->
158 {noreply, State}.
159
4872cd7 @vinoski delete ets table in terminate
vinoski authored
160 terminate(_Reason, #state{port = Port, caller_tbl = CallerTable}) ->
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
161 erlang:port_close(Port),
162 receive {'EXIT', Port, _Reason} -> ok
163 after 0 -> ok
164 end,
4872cd7 @vinoski delete ets table in terminate
vinoski authored
165 ets:delete(CallerTable),
166 ok.
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
167
168 code_change(_OldVsn, Data, _Extra) ->
169 {ok, Data}.
170
4aa146b @vinoski fix dialyzer problems in yaws_sendfile changes
vinoski authored
171 do_send(_Out, _SocketFd, _Filename, _Offset, Count) when Count =< 0 ->
172 {ok, 0};
d75acaf @vinoski restore overflow handling dropped in previous commit
vinoski authored
173 do_send(Out, SocketFd, Filename, Offset, Count) ->
174 Call = list_to_binary([<<Offset:64, Count:64, SocketFd:32>>,
175 Filename, <<0:8>>]),
176 case gen_server:call(?MODULE, {send, SocketFd, Call}, infinity) of
177 {error, eoverflow} ->
178 compat_send(Out, Filename, Offset, Count);
179 Else ->
180 Else
181 end.
182
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
183 -else.
184
185 enabled() ->
186 false.
2150708 @tuncer Use file:sendfile/5 if available
tuncer authored
187 send(Out, Filename, Offset, Count) ->
188 compat_send(Out, Filename, Offset, Count).
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
189 start_link() ->
190 ignore.
191 start() ->
192 ignore.
193 stop() ->
194 ok.
195
d75acaf @vinoski restore overflow handling dropped in previous commit
vinoski authored
196 -endif.
197
4aa146b @vinoski fix dialyzer problems in yaws_sendfile changes
vinoski authored
198 compat_send(Out, Filename, Offset, Count0) ->
199 Count = case Count0 of
200 0 -> all;
201 _ -> Count0
202 end,
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
203 case file:open(Filename, [read, binary, raw]) of
204 {ok, Fd} ->
205 file:position(Fd, {bof, Offset}),
206 ChunkSize = (get(gc))#gconf.large_file_chunk_size,
ea529f1 Fix compat_send/3 to return the number of bytes sent
Christopher Faulet authored
207 Ret = loop_send(Fd, ChunkSize, file:read(Fd, ChunkSize), Out,
208 Count, 0),
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
209 file:close(Fd),
210 Ret;
211 Err ->
212 Err
cd2deb9 @vinoski Add sendfile linked-in driver for use on Linux and OS X. It is not yet t...
vinoski authored
213 end.
214
ea529f1 Fix compat_send/3 to return the number of bytes sent
Christopher Faulet authored
215 loop_send(Fd, ChunkSize, {ok, Bin}, Out, all, BytesSent) ->
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
216 case gen_tcp:send(Out, Bin) of
217 ok ->
ea529f1 Fix compat_send/3 to return the number of bytes sent
Christopher Faulet authored
218 loop_send(Fd, ChunkSize, file:read(Fd, ChunkSize), Out, all,
219 BytesSent+size(Bin));
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
220 Err ->
221 Err
222 end;
ea529f1 Fix compat_send/3 to return the number of bytes sent
Christopher Faulet authored
223 loop_send(_Fd, _ChunkSize, eof, _Out, _, BytesSent) ->
224 {ok, BytesSent};
225 loop_send(Fd, ChunkSize, {ok, Bin}, Out, Count, BytesSent) ->
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
226 Sz = size(Bin),
227 if Sz < Count ->
228 case gen_tcp:send(Out, Bin) of
229 ok ->
230 loop_send(Fd, ChunkSize, file:read(Fd, ChunkSize),
ea529f1 Fix compat_send/3 to return the number of bytes sent
Christopher Faulet authored
231 Out, Count-Sz, BytesSent+Sz);
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
232 Err ->
233 Err
12471ad @klacke added configure support for sendfile
authored
234 end;
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
235 Sz == Count ->
ea529f1 Fix compat_send/3 to return the number of bytes sent
Christopher Faulet authored
236 case gen_tcp:send(Out, Bin) of
237 ok -> {ok, BytesSent+Sz};
238 Err -> Err
239 end;
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
240 Sz > Count ->
241 <<Deliver:Count/binary , _/binary>> = Bin,
ea529f1 Fix compat_send/3 to return the number of bytes sent
Christopher Faulet authored
242 case gen_tcp:send(Out, Deliver) of
243 ok -> {ok, BytesSent+Count};
244 Err -> Err
245 end
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
246 end;
ea529f1 Fix compat_send/3 to return the number of bytes sent
Christopher Faulet authored
247 loop_send(_Fd, _, Err, _, _, _) ->
6a4c4e7 @vinoski change sendfile driver handler to a gen_server
vinoski authored
248 Err.
2150708 @tuncer Use file:sendfile/5 if available
tuncer authored
249
250 -endif.
Something went wrong with that request. Please try again.