blob: e97acff93eba17d50f423956051be6b90b86c36d [file] [log] [blame]
David Reissea2cba82009-03-30 21:35:00 +00001%%
2%% Licensed to the Apache Software Foundation (ASF) under one
3%% or more contributor license agreements. See the NOTICE file
4%% distributed with this work for additional information
5%% regarding copyright ownership. The ASF licenses this file
6%% to you under the Apache License, Version 2.0 (the
7%% "License"); you may not use this file except in compliance
8%% with the License. You may obtain a copy of the License at
9%%
10%% http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing,
13%% software distributed under the License is distributed on an
14%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15%% KIND, either express or implied. See the License for the
16%% specific language governing permissions and limitations
17%% under the License.
18%%
19
David Reiss0c8cb4a2008-06-11 01:12:52 +000020-module(thrift_server).
21
22-behaviour(gen_server).
23
24%% API
25-export([start_link/3, stop/1, take_socket/2]).
26
27%% gen_server callbacks
Sergei Elin45764092022-09-23 23:21:31 +030028-export([
29 init/1,
30 handle_call/3,
31 handle_cast/2,
32 handle_info/2,
33 terminate/2,
34 code_change/3
35]).
David Reiss0c8cb4a2008-06-11 01:12:52 +000036
37-define(SERVER, ?MODULE).
38
Sergei Elin45764092022-09-23 23:21:31 +030039-record(state, {
40 listen_socket :: gen_tcp:socket(),
41 acceptor_ref :: term(),
42 service :: module(),
43 handler :: module()
44}).
David Reiss0c8cb4a2008-06-11 01:12:52 +000045
46%%====================================================================
47%% API
48%%====================================================================
49%%--------------------------------------------------------------------
50%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
51%% Description: Starts the server
52%%--------------------------------------------------------------------
53start_link(Port, Service, HandlerModule) when is_integer(Port), is_atom(HandlerModule) ->
54 gen_server:start_link({local, ?SERVER}, ?MODULE, {Port, Service, HandlerModule}, []).
55
56%%--------------------------------------------------------------------
57%% Function: stop(Pid) -> ok, {error, Reason}
58%% Description: Stops the server.
59%%--------------------------------------------------------------------
60stop(Pid) when is_pid(Pid) ->
61 gen_server:call(Pid, stop).
62
David Reiss0c8cb4a2008-06-11 01:12:52 +000063take_socket(Server, Socket) ->
64 gen_server:call(Server, {take_socket, Socket}).
65
David Reiss0c8cb4a2008-06-11 01:12:52 +000066%%====================================================================
67%% gen_server callbacks
68%%====================================================================
69
70%%--------------------------------------------------------------------
71%% Function: init(Args) -> {ok, State} |
72%% {ok, State, Timeout} |
73%% ignore |
74%% {stop, Reason}
75%% Description: Initiates the server
76%%--------------------------------------------------------------------
77init({Port, Service, Handler}) ->
Sergei Elin45764092022-09-23 23:21:31 +030078 {ok, Socket} = gen_tcp:listen(
79 Port,
80 [
81 binary,
82 {packet, 0},
83 {active, false},
84 {nodelay, true},
85 {reuseaddr, true}
86 ]
87 ),
David Reiss0c8cb4a2008-06-11 01:12:52 +000088 {ok, Ref} = prim_inet:async_accept(Socket, -1),
Sergei Elin45764092022-09-23 23:21:31 +030089 {ok, #state{
90 listen_socket = Socket,
91 acceptor_ref = Ref,
92 service = Service,
93 handler = Handler
94 }}.
David Reiss0c8cb4a2008-06-11 01:12:52 +000095
96%%--------------------------------------------------------------------
97%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
98%% {reply, Reply, State, Timeout} |
99%% {noreply, State} |
100%% {noreply, State, Timeout} |
101%% {stop, Reason, Reply, State} |
102%% {stop, Reason, State}
103%% Description: Handling call messages
104%%--------------------------------------------------------------------
105handle_call(stop, _From, State) ->
106 {stop, stopped, ok, State};
David Reiss0c8cb4a2008-06-11 01:12:52 +0000107handle_call({take_socket, Socket}, {FromPid, _Tag}, State) ->
108 Result = gen_tcp:controlling_process(Socket, FromPid),
109 {reply, Result, State}.
110
111%%--------------------------------------------------------------------
112%% Function: handle_cast(Msg, State) -> {noreply, State} |
113%% {noreply, State, Timeout} |
114%% {stop, Reason, State}
115%% Description: Handling cast messages
116%%--------------------------------------------------------------------
117handle_cast(_Msg, State) ->
118 {noreply, State}.
119
120%%--------------------------------------------------------------------
121%% Function: handle_info(Info, State) -> {noreply, State} |
122%% {noreply, State, Timeout} |
123%% {stop, Reason, State}
124%% Description: Handling all non call/cast messages
125%%--------------------------------------------------------------------
Sergei Elin45764092022-09-23 23:21:31 +0300126handle_info(
127 {inet_async, ListenSocket, Ref, {ok, ClientSocket}},
128 State = #state{
129 listen_socket = ListenSocket,
130 acceptor_ref = Ref,
131 service = Service,
132 handler = Handler
133 }
134) ->
David Reiss0c8cb4a2008-06-11 01:12:52 +0000135 case set_sockopt(ListenSocket, ClientSocket) of
136 ok ->
137 %% New client connected - start processor
138 start_processor(ClientSocket, Service, Handler),
139 {ok, NewRef} = prim_inet:async_accept(ListenSocket, -1),
140 {noreply, State#state{acceptor_ref = NewRef}};
141 {error, Reason} ->
Sergei Elin45764092022-09-23 23:21:31 +0300142 error_logger:error_msg(
143 "Couldn't set socket opts: ~p~n",
144 [Reason]
145 ),
David Reiss0c8cb4a2008-06-11 01:12:52 +0000146 {stop, Reason, State}
147 end;
David Reiss5ed313d2010-08-30 22:05:57 +0000148handle_info({inet_async, _ListenSocket, _Ref, Error}, State) ->
David Reiss0c8cb4a2008-06-11 01:12:52 +0000149 error_logger:error_msg("Error in acceptor: ~p~n", [Error]),
150 {stop, Error, State};
David Reiss0c8cb4a2008-06-11 01:12:52 +0000151handle_info(_Info, State) ->
152 {noreply, State}.
153
154%%--------------------------------------------------------------------
155%% Function: terminate(Reason, State) -> void()
156%% Description: This function is called by a gen_server when it is about to
157%% terminate. It should be the opposite of Module:init/1 and do any necessary
158%% cleaning up. When it returns, the gen_server terminates with Reason.
159%% The return value is ignored.
160%%--------------------------------------------------------------------
161terminate(_Reason, _State) ->
162 ok.
163
164%%--------------------------------------------------------------------
165%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
166%% Description: Convert process state when code is changed
167%%--------------------------------------------------------------------
168code_change(_OldVsn, State, _Extra) ->
169 {ok, State}.
170
171%%--------------------------------------------------------------------
172%%% Internal functions
173%%--------------------------------------------------------------------
174set_sockopt(ListenSocket, ClientSocket) ->
175 true = inet_db:register_socket(ClientSocket, inet_tcp),
Sergei Elin45764092022-09-23 23:21:31 +0300176 case
177 prim_inet:getopts(
178 ListenSocket,
179 [active, nodelay, keepalive, delay_send, priority, tos]
180 )
181 of
David Reiss0c8cb4a2008-06-11 01:12:52 +0000182 {ok, Opts} ->
183 case prim_inet:setopts(ClientSocket, Opts) of
Sergei Elin45764092022-09-23 23:21:31 +0300184 ok ->
185 ok;
186 Error ->
187 gen_tcp:close(ClientSocket),
188 Error
David Reiss0c8cb4a2008-06-11 01:12:52 +0000189 end;
190 Error ->
191 gen_tcp:close(ClientSocket),
192 Error
193 end.
194
195start_processor(Socket, Service, Handler) ->
196 Server = self(),
197
198 ProtoGen = fun() ->
Sergei Elin45764092022-09-23 23:21:31 +0300199 % Become the controlling process
200 ok = take_socket(Server, Socket),
201 {ok, SocketTransport} = thrift_socket_transport:new(Socket),
202 {ok, BufferedTransport} = thrift_buffered_transport:new(SocketTransport),
203 {ok, Protocol} = thrift_binary_protocol:new(BufferedTransport),
204 {ok, Protocol}
205 end,
David Reiss0c8cb4a2008-06-11 01:12:52 +0000206
David Reiss37dbfef2008-06-11 01:16:01 +0000207 spawn(thrift_processor, init, [{Server, ProtoGen, Service, Handler}]).