87 lines
2.8 KiB
Erlang
87 lines
2.8 KiB
Erlang
% Copyright (c) Camden Dixie O'Brien
|
|
% SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
-module(session_server).
|
|
-behaviour(gen_server).
|
|
|
|
-export([start_link/1]).
|
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
|
terminate/2, code_change/3]).
|
|
|
|
start_link(Socket) ->
|
|
gen_server:start_link(?MODULE, Socket, []).
|
|
|
|
init(Socket) ->
|
|
ok = ssl:setopts(Socket, [{active, true}]),
|
|
process_flag(trap_exit, true),
|
|
{ok, #{socket => Socket, transactions => #{}}}.
|
|
|
|
handle_call(_Request, _From, State) ->
|
|
{reply, ok, State}.
|
|
|
|
handle_cast(_Msg, State) ->
|
|
{noreply, State}.
|
|
|
|
handle_info({ssl, Socket, Data}, State = #{transactions := Transactions}) ->
|
|
case 'StudySystemProtocol':decode('Request', Data) of
|
|
{ok, {'Request', TransactionId, RequestBody}} ->
|
|
Pid = spawn_link(fun() -> handle_request(RequestBody) end),
|
|
NewTransactions = maps:put(Pid, TransactionId, Transactions),
|
|
{noreply, State#{transactions := NewTransactions}};
|
|
{error, {asn1, _Reason}} ->
|
|
send(Socket, -1, {error, invalidRequest}),
|
|
{noreply, State}
|
|
end;
|
|
handle_info({'EXIT', Pid, Reason},
|
|
State = #{socket := Socket, transactions := Transactions})
|
|
when is_map_key(Pid, Transactions) ->
|
|
TransactionId = maps:get(Pid, Transactions),
|
|
Response = case Reason of
|
|
{response, Value} -> Value;
|
|
_ ->
|
|
io:format("Error handling request: ~p~n", [Reason]),
|
|
{error, serverError}
|
|
end,
|
|
send(Socket, TransactionId, Response),
|
|
{noreply, State#{transactions := maps:remove(Pid, Transactions)}};
|
|
handle_info({ssl_closed, _Socket}, State) ->
|
|
{stop, normal, State};
|
|
handle_info({ssl_error, _Socket, _Reason}, State) ->
|
|
{stop, normal, State};
|
|
handle_info(_Info, State) ->
|
|
{noreply, State}.
|
|
|
|
terminate(_Reason, #{socket := Socket}) ->
|
|
ssl:close(Socket).
|
|
|
|
code_change(_OldVsn, State, _Extra) ->
|
|
{ok, State}.
|
|
|
|
handle_request(Request) ->
|
|
timer:kill_after(500),
|
|
exit(map_request(Request)).
|
|
|
|
map_request({ping, 'NULL'}) ->
|
|
{response, {ack, 'NULL'}};
|
|
map_request({listActivities, 'NULL'}) ->
|
|
{activities, Activities} = subject_router:get_activities(),
|
|
SortedActivities = lists:reverse(lists:keysort(3, Activities)),
|
|
{response, {activities,
|
|
[{'Activity', Subject, Type, round(Priority * 100)}
|
|
|| {Subject, Type, Priority} <- SortedActivities]}};
|
|
map_request({logSession, {'Session', Subject, Type, Timestamp, Minutes}}) ->
|
|
Session = {unicode:characters_to_list(Subject),
|
|
Type, Timestamp, Minutes},
|
|
case subject_router:log_session(Session) of
|
|
ok -> {response, {ack, 'NULL'}};
|
|
{error, _Error} -> {response, {error, invalidArguments}}
|
|
end;
|
|
map_request(_) ->
|
|
{response, {error, invalidArguments}}.
|
|
|
|
send(Socket, TransactionId, Response) ->
|
|
{ok, Encoded} = 'StudySystemProtocol':encode(
|
|
'Response',
|
|
{'Response', TransactionId, Response}),
|
|
ok = ssl:send(Socket, Encoded).
|