Add gemini connection endpoint
This commit is contained in:
parent
6622913d52
commit
53bfdbd75d
|
@ -23,8 +23,8 @@ defmodule Chronoscope.Gemini do
|
|||
|> Enum.map(fn {_, pid, _, _} -> @genserver.call(pid, :list) end)
|
||||
end
|
||||
|
||||
def remove(host, port) do
|
||||
name = client_name(%{host: host, port: port})
|
||||
def remove(host, port, path) do
|
||||
name = client_name(%{host: host, port: port, path: path})
|
||||
|
||||
case @registry.lookup(Gemini.Registry, name) do
|
||||
[{pid, _}] -> {:ok, @genserver.call(pid, :terminate)}
|
||||
|
|
|
@ -18,7 +18,7 @@ defmodule Chronoscope.Gemini.Client do
|
|||
{:ok,
|
||||
%{
|
||||
resource: resource,
|
||||
reponse: {:error, "initializing"},
|
||||
response: {:error, "initializing"},
|
||||
last_request: DateTime.add(now, -@interval_in_seconds, :second)
|
||||
}}
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
defmodule Chronoscope.Gemini.Response do
|
||||
def parse(response) do
|
||||
# TODO
|
||||
%{status: 20, type: "text/gemini", body: response}
|
||||
%{status: 20, mime_type: "text/gemini", body: to_string(response)}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
defmodule ChronoscopeWeb.API.V1.Gemini.ConnectionController do
|
||||
use ChronoscopeWeb, :controller
|
||||
|
||||
require Logger
|
||||
|
||||
alias Chronoscope.Gemini
|
||||
|
||||
@default_port 1965
|
||||
@default_path "/"
|
||||
@max_host_length 255
|
||||
@gemini Application.compile_env(:chronoscope, :gemini, Gemini)
|
||||
|
||||
def get(conn, %{"host" => host, "port" => port, "path" => path}) do
|
||||
try do
|
||||
handle_get(conn, %{host: host, port: String.to_integer(port), path: path})
|
||||
rescue
|
||||
ArgumentError -> bad_request_response(conn, "invalid port")
|
||||
end
|
||||
end
|
||||
|
||||
def get(conn, %{"host" => host, "path" => path}) do
|
||||
handle_get(conn, %{host: host, port: @default_port, path: path})
|
||||
end
|
||||
|
||||
def get(conn, %{"host" => host, "port" => port}) do
|
||||
handle_get(conn, %{host: host, port: port, path: @default_path})
|
||||
end
|
||||
|
||||
def get(conn, %{"host" => host}) do
|
||||
handle_get(conn, %{host: host, port: @default_port, path: @default_path})
|
||||
end
|
||||
|
||||
def get(conn, _params) do
|
||||
bad_request_response(conn, "missing host")
|
||||
end
|
||||
|
||||
defp handle_get(conn, %{host: host, port: port, path: path}) when port > 0 and port < 65536 do
|
||||
case connect(host, port, path) do
|
||||
{:ok, response} ->
|
||||
json(conn, %{status: :ok, response: format_response(response)})
|
||||
|
||||
{:error, error} ->
|
||||
json(conn, %{status: :error, reason: to_string(error)})
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_get(conn, _params) do
|
||||
bad_request_response(conn, "port out of range")
|
||||
end
|
||||
|
||||
defp connect(host, port, path) do
|
||||
# TODO - max path length
|
||||
host
|
||||
|> String.slice(0, @max_host_length)
|
||||
|> @gemini.connect(port, path)
|
||||
end
|
||||
|
||||
defp format_response(response) do
|
||||
Map.take(response, [:status, :mime_type, :body])
|
||||
end
|
||||
|
||||
defp bad_request_response(conn, message) do
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: message})
|
||||
end
|
||||
end
|
|
@ -32,6 +32,12 @@ defmodule ChronoscopeWeb.Router do
|
|||
get "/key-establishment", KeyEstablishmentController, :get
|
||||
end
|
||||
|
||||
scope "/api/v1/gemini", ChronoscopeWeb.API.V1.Gemini do
|
||||
pipe_through :api
|
||||
|
||||
get "/connect", ConnectionController, :get
|
||||
end
|
||||
|
||||
# Enable LiveDashboard and Swoosh mailbox preview in development
|
||||
if Application.compile_env(:chronoscope, :dev_routes) do
|
||||
# If you want to use the LiveDashboard in production, you should put
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
defmodule Chronoscope.Gemini.ClientTest do
|
||||
use Chronoscope.Case, async: true
|
||||
|
||||
alias Chronoscope.Certificate
|
||||
alias Chronoscope.DateTimeMock
|
||||
alias Chronoscope.Gemini.Client
|
||||
alias Chronoscope.SSLMock
|
||||
|
||||
import Mox
|
||||
|
||||
setup :verify_on_exit!
|
||||
|
||||
setup _tags do
|
||||
DateTimeMock
|
||||
|> stub(:utc_now, fn -> ~U[2024-04-21 01:23:45Z] end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "Chronoscope.Gemini.Client.init()" do
|
||||
test "initializes successfully" do
|
||||
assert Client.init(%{host: "localhost", port: 4444, path: "/"}) ==
|
||||
{:ok,
|
||||
%{
|
||||
resource: %{host: "localhost", port: 4444, path: "/"},
|
||||
response: {:error, "initializing"},
|
||||
last_request: ~U[2024-04-21 01:23:15Z]
|
||||
}}
|
||||
end
|
||||
end
|
||||
|
||||
describe "Chronoscope.Gemini.Client.handle_call()" do
|
||||
test ":terminate" do
|
||||
assert Client.handle_call(:terminate, nil, %{state: true}) == {:stop, :normal, self(), %{state: true}}
|
||||
end
|
||||
|
||||
test ":list" do
|
||||
assert Client.handle_call(:list, nil, %{state: true}) == {:reply, %{state: true}, %{state: true}}
|
||||
end
|
||||
|
||||
test ":connect - cached" do
|
||||
assert Client.handle_call(:connect, nil, %{
|
||||
resource: %{host: "localhost", port: 4444, path: "/"},
|
||||
response: {:error, "initializing"},
|
||||
last_request: ~U[2024-04-21 01:23:45Z]
|
||||
}) ==
|
||||
{:reply, {:error, "initializing"},
|
||||
%{
|
||||
resource: %{host: "localhost", port: 4444, path: "/"},
|
||||
response: {:error, "initializing"},
|
||||
last_request: ~U[2024-04-21 01:23:45Z]
|
||||
}}
|
||||
end
|
||||
|
||||
test ":connect - not cached" do
|
||||
peercert = peercert()
|
||||
peercert_expiration = Certificate.expiration_date(peercert)
|
||||
|
||||
SSLMock
|
||||
|> expect(:connect, fn ~c"localhost", 4444, _, _ -> {:ok, :socket} end)
|
||||
|> expect(:send, fn :socket, _ -> send_ssl_response([]) end)
|
||||
|> expect(:peercert, fn :socket -> {:ok, peercert} end)
|
||||
|> expect(:close, fn :socket -> :ok end)
|
||||
|
||||
assert {:reply, {:ok, %{cert_expiration: ^peercert_expiration}},
|
||||
%{
|
||||
resource: %{host: "localhost", port: 4444, path: "/"},
|
||||
response: {:ok, %{cert_expiration: ^peercert_expiration}},
|
||||
last_request: ~U[2024-04-21 01:23:45Z]
|
||||
}} =
|
||||
Client.handle_call(:connect, nil, %{
|
||||
resource: %{host: "localhost", port: 4444, path: "/"},
|
||||
response: {:error, "initializing"},
|
||||
last_request: ~U[2024-04-21 01:23:00Z]
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,9 +3,9 @@ defmodule Chronoscope.NTS.ClientTest do
|
|||
|
||||
alias Chronoscope.Certificate
|
||||
alias Chronoscope.DateTimeMock
|
||||
alias Chronoscope.NTS.Client
|
||||
alias Chronoscope.SSLMock
|
||||
|
||||
import Chronoscope.NTS.Client
|
||||
import Mox
|
||||
|
||||
setup :verify_on_exit!
|
||||
|
@ -19,7 +19,7 @@ defmodule Chronoscope.NTS.ClientTest do
|
|||
|
||||
describe "Chronoscope.NTS.Client.init()" do
|
||||
test "initializes successfully" do
|
||||
assert init(%{host: "localhost", port: 3333}) ==
|
||||
assert Client.init(%{host: "localhost", port: 3333}) ==
|
||||
{:ok,
|
||||
%{
|
||||
server: %{host: "localhost", port: 3333},
|
||||
|
@ -31,26 +31,24 @@ defmodule Chronoscope.NTS.ClientTest do
|
|||
|
||||
describe "Chronoscope.NTS.Client.handle_call()" do
|
||||
test ":terminate" do
|
||||
assert handle_call(:terminate, nil, %{state: true}) == {:stop, :normal, self(), %{state: true}}
|
||||
assert Client.handle_call(:terminate, nil, %{state: true}) == {:stop, :normal, self(), %{state: true}}
|
||||
end
|
||||
|
||||
test ":list" do
|
||||
assert handle_call(:list, nil, %{state: true}) == {:reply, %{state: true}, %{state: true}}
|
||||
assert Client.handle_call(:list, nil, %{state: true}) == {:reply, %{state: true}, %{state: true}}
|
||||
end
|
||||
|
||||
test ":key_establishment - cached" do
|
||||
assert handle_call(:key_establishment, nil, %{
|
||||
host: "localhost",
|
||||
assert Client.handle_call(:key_establishment, nil, %{
|
||||
server: %{host: "localhost", port: 3333},
|
||||
key_establishment_response: {:error, "initializing"},
|
||||
last_key_establishment: ~U[2024-03-31 01:23:45Z],
|
||||
port: 3333
|
||||
last_key_establishment: ~U[2024-03-31 01:23:45Z]
|
||||
}) ==
|
||||
{:reply, {:error, "initializing"},
|
||||
%{
|
||||
host: "localhost",
|
||||
server: %{host: "localhost", port: 3333},
|
||||
key_establishment_response: {:error, "initializing"},
|
||||
last_key_establishment: ~U[2024-03-31 01:23:45Z],
|
||||
port: 3333
|
||||
last_key_establishment: ~U[2024-03-31 01:23:45Z]
|
||||
}}
|
||||
end
|
||||
|
||||
|
@ -70,7 +68,7 @@ defmodule Chronoscope.NTS.ClientTest do
|
|||
key_establishment_response: {:ok, %{cert_expiration: ^peercert_expiration}},
|
||||
last_key_establishment: ~U[2024-03-31 01:23:45Z]
|
||||
}} =
|
||||
handle_call(:key_establishment, nil, %{
|
||||
Client.handle_call(:key_establishment, nil, %{
|
||||
server: %{host: "localhost", port: 3333},
|
||||
key_establishment_response: {:error, "initializing"},
|
||||
last_key_establishment: ~U[2024-03-31 01:23:00Z]
|
||||
|
|
Loading…
Reference in New Issue