chronoscope/lib/chronoscope/gemini/connection_client.ex

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