Skip to content

Commit

Permalink
feat: HELP command
Browse files Browse the repository at this point in the history
  • Loading branch information
sntran committed Mar 6, 2021
1 parent 8bd780f commit ceaa051
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/gen_nntp.ex
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ defmodule GenNNTP do
when number: non_neg_integer(),
arg: :gen_nttp.message_id() | number

@callback handle_HELP(state) :: {:ok, help_text :: String.t(), state}

@callback handle_command(command :: String.t(), state) ::
{:reply, response :: any(), state} |
{:noreply, state} |
Expand Down
19 changes: 19 additions & 0 deletions src/gen_nntp.erl
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@
when Number :: non_neg_integer(),
Arg :: message_id() | Number.

-callback handle_HELP(state()) -> {ok, HelpText :: [binary()], state()}.

-callback handle_command(Command :: binary(), state()) ->
{reply, Response :: binary(), state()}
| {noreply, state()}
Expand Down Expand Up @@ -473,6 +475,23 @@ handle_command(<<"ARTICLE ", Arg/binary>> = Cmd, Client) ->

{reply, Reply, Client#client{state = NewState}};

% This command provides a short summary of the commands that are
% understood by this implementation of the server. The help text will
% be presented as a multi-line data block following the 100 response
% code. This text is not guaranteed to be in any particular format (but must
% be UTF-8) and MUST NOT be used by clients as a replacement for the
% CAPABILITIES command
handle_command(<<"HELP">>, Client) ->
#client{module = Module, state = State} = Client,
{ok, Help, NewState} = Module:handle_HELP(State),

Reply = [
<<"100 Help text follows\r\n">>,
Help, <<"\r\n">>,
<<".">>
],
{reply, Reply, Client#client{state = NewState}};

% The client uses the QUIT command to terminate the session. The server
% MUST acknowledge the QUIT command and then close the connection to
% the client.
Expand Down
60 changes: 60 additions & 0 deletions test/gen_nntp_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,66 @@ defmodule GenNNTPTest do

end

describe "@callback handle_HELP/1" do
setup do
help_text = String.trim("""
This is some help text. There is no specific\r
formatting requirement for this test, though\r
it is customary for it to list the valid commands\r
and give a brief definition of what they do.
""")

TestNNTPServer.start(
handle_HELP: fn(state) ->
Kernel.send(:tester, {:called_back, :handle_HELP, 1})

{:ok, help_text, state}
end
)

{:ok, socket, _greeting} = GenNNTP.connect()

%{socket: socket, help_text: help_text}
end

test "is called when the client asks for it", %{socket: socket} do
refute_receive(
{:called_back, :handle_HELP, 1},
100,
"@callback handle_HELP/1 should not be called when client has not asked for it"
)

:ok = :gen_tcp.send(socket, "HELP\r\n")

assert_receive(
{:called_back, :handle_HELP, 1},
100,
"@callback handle_HELP/1 was not called"
)
end

test "responds with `100` and a multi-line data block when the client asks for it", context do
%{socket: socket, help_text: help_text} = context

:ok = :gen_tcp.send(socket, "HELP\r\n")

{:ok, response} = :gen_tcp.recv(socket, 0, 1000)
assert response =~ ~r/^100 /

# Help text line by line.
help_text
|> String.split("\r\n")
|> Enum.each(fn line ->
{:ok, response} = :gen_tcp.recv(socket, 0, 1000)
assert response === "#{line}\r\n"
end)

# Then the termination line
{:ok, response} = :gen_tcp.recv(socket, 0, 1000)
assert response === ".\r\n"
end
end

describe "command/3" do

setup context do
Expand Down
13 changes: 13 additions & 0 deletions test/support/test_nntp_server.ex
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,19 @@ defmodule TestNNTPServer do
end
end

@impl GenNNTP
def handle_HELP(client) do
state = client[:state]

case maybe_apply(client, :handle_HELP, [state], {:ok, "", state}) do
{:ok, help_text, state} ->
client = Keyword.put(client, :state, state)
{:ok, help_text, client}
other ->
other
end
end

@impl GenNNTP
def handle_command(_command, state) do
{:noreply, state}
Expand Down

0 comments on commit ceaa051

Please sign in to comment.