/
yaws_sendfile.erl
107 lines (99 loc) · 3.28 KB
/
yaws_sendfile.erl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
%%% File : yaws_sendfile.erl
%%% Author : Steve Vinoski <vinoski@ieee.org>
%%% Description : interface to sendfile linked-in driver for Yaws
%%% Created : 9 Nov 2008 by Steve Vinoski <vinoski@ieee.org>
-module(yaws_sendfile).
-export([start_link/0, init/1, stop/0, send/2, send/3, send/4]).
-include_lib("kernel/include/file.hrl").
start_link() ->
Shlib = "yaws_sendfile_drv",
Dir = case yaws_generated:is_local_install() of
true ->
filename:dirname(code:which(?MODULE)) ++ "/../priv/lib";
false ->
%% ignore dialyzer on this one
PrivDir = code:priv_dir(yaws),
filename:join(PrivDir,"lib")
end,
case erl_ddll:load_driver(Dir, Shlib) of
ok -> ok;
{error, already_loaded} -> ok;
_ -> exit({error, could_not_load_driver})
end,
{ok, spawn_link(?MODULE, init, [Shlib])}.
init(Shlib) ->
register(?MODULE, self()),
process_flag(trap_exit, true),
Port = open_port({spawn, Shlib}, [binary]),
loop(Port).
stop() ->
?MODULE ! stop,
unregister(?MODULE),
ok.
send(Out, Filename) ->
send(Out, Filename, 0, 0).
send(Out, Filename, Offset) ->
send(Out, Filename, Offset, 0).
send(Out, Filename, Offset, Count) ->
Count2 = case Count of
0 ->
case file:read_file_info(Filename) of
{ok, #file_info{size = Size}} ->
Size - Offset;
Error ->
Error
end;
_ ->
Count
end,
case Count2 of
{error, _}=Error2 ->
Error2;
_ ->
case prim_inet:getfd(Out) of
{ok, Socket_fd} ->
call_port(
Socket_fd,
list_to_binary(
[<<Offset:64, Count2:64, Socket_fd:32>>, Filename, <<0:8>>]));
Error3 ->
Error3
end
end.
call_port(Socket_id, Msg) ->
?MODULE ! {call, self(), Socket_id, Msg},
receive
{?MODULE, Reply} ->
Reply
end.
loop(Port) ->
receive
{call, Caller, Id, Msg} ->
put(Id, Caller),
erlang:port_command(Port, Msg),
loop(Port);
{Port, {data, <<Cnt:64, Id:32, Res:8, Err/binary>>}} ->
Response = case Res of
1 ->
{ok, Cnt};
0 ->
{error,
list_to_atom(
lists:takewhile(fun(El) -> El =/= 0 end,
binary_to_list(Err)))}
end,
Caller = erase(Id),
Caller ! {?MODULE, Response},
loop(Port);
stop ->
erlang:port_close(Port),
receive {'EXIT', Port, _Reason} -> ok
after 0 -> ok
end;
{'EXIT', Port, Posix_error} ->
error_logger:format("Fatal error: sendfile port died, error ~p~n", [Posix_error]),
exit(Posix_error);
{'EXIT', error, Reason} ->
error_logger:format("Fatal error: sendfile driver failure: ~p~n", [Reason]),
exit(Reason)
end.