77 lines
2.2 KiB
Elixir
77 lines
2.2 KiB
Elixir
defmodule Chronoscope.Gemini.ConnectionClient do
|
|
require Logger
|
|
|
|
alias Chronoscope.Certificate
|
|
alias Chronoscope.Gemini.Request
|
|
alias Chronoscope.Gemini.Response
|
|
|
|
@timeout_in_milliseconds 3000
|
|
|
|
@ssl Application.compile_env(:chronoscope, :ssl, :ssl)
|
|
|
|
def connect(%{host: host, port: port, path: _} = resource) do
|
|
case ssl_connect(host, port) do
|
|
{:ok, socket} -> make_request(socket, resource)
|
|
{:error, {:tls_alert, {:handshake_failure, error}}} -> {:error, String.trim("#{error}")}
|
|
{:error, {:tls_alert, {:no_application_protocol, error}}} -> {:error, String.trim("#{error}")}
|
|
{:error, :timeout} -> {:error, :timeout}
|
|
{:error, error} -> {:error, inspect(error)}
|
|
error -> {:error, inspect(error)}
|
|
end
|
|
end
|
|
|
|
defp ssl_connect(host, port) do
|
|
host
|
|
|> String.to_charlist()
|
|
|> @ssl.connect(port, tls_options(host), @timeout_in_milliseconds)
|
|
end
|
|
|
|
defp tls_options(host) do
|
|
host
|
|
|> :tls_certificate_check.options()
|
|
|> Keyword.put(:verify_fun, {verify_fun(host), nil})
|
|
end
|
|
|
|
defp verify_fun(hostname) do
|
|
hostname_charlist = String.to_charlist(hostname)
|
|
|
|
fn
|
|
certificate, {:bad_cert, :selfsigned_peer}, _state ->
|
|
:ssl_verify_hostname.verify_fun(certificate, :valid_peer, check_hostname: hostname_charlist)
|
|
{:valid, :selfsigned_peer}
|
|
|
|
certificate, event, _state ->
|
|
IO.inspect(event)
|
|
:ssl_verify_hostname.verify_fun(certificate, event, check_hostname: hostname_charlist)
|
|
end
|
|
end
|
|
|
|
defp make_request(socket, url) do
|
|
:ok = @ssl.send(socket, Request.create(url))
|
|
{:ok, peercert} = @ssl.peercert(socket)
|
|
|
|
receive do
|
|
{:ssl, _socket, response} ->
|
|
@ssl.close(socket)
|
|
parse_response(response, peercert)
|
|
|
|
msg ->
|
|
@ssl.close(socket)
|
|
Logger.error("received unexpected message: #{inspect(msg)}")
|
|
{:error, :no_response}
|
|
after
|
|
@timeout_in_milliseconds ->
|
|
@ssl.close(socket)
|
|
Logger.error("timed out waiting for response")
|
|
{:error, :timeout}
|
|
end
|
|
end
|
|
|
|
defp parse_response(response, peercert) do
|
|
response
|
|
|> Response.parse()
|
|
|> Map.put(:cert_expiration, Certificate.expiration_date(peercert))
|
|
|> then(&{:ok, &1})
|
|
end
|
|
end
|