Packages
mojito_fresha
0.7.13
Mojito client but with castore updated, forked from https://github.com/surgeventures/mojito
Current section
Files
Jump to
Current section
Files
lib/mojito/conn.ex
defmodule Mojito.Conn do
@moduledoc false
alias Mojito.{Error, Telemetry, Utils}
defstruct conn: nil,
protocol: nil,
hostname: nil,
port: nil
@type t :: %Mojito.Conn{}
@doc ~S"""
Connects to the specified endpoint, returning a connection to the server.
No requests are made.
"""
@spec connect(String.t(), Keyword.t()) :: {:ok, t} | {:error, any}
def connect(url, opts \\ []) do
with {:ok, protocol, hostname, port} <- Utils.decompose_url(url) do
connect(protocol, hostname, port, opts)
end
end
@doc ~S"""
Closes a connection
"""
@spec close(t) :: :ok
def close(conn) do
Mint.HTTP.close(conn.conn)
:ok
end
@doc ~S"""
Connects to the server specified in the given URL,
returning a connection to the server. No requests are made.
"""
@spec connect(String.t(), String.t(), non_neg_integer, Keyword.t()) ::
{:ok, t} | {:error, any}
def connect(protocol, hostname, port, opts \\ []) do
with meta <- %{host: hostname, port: port},
start_time <- Telemetry.start(:connect, meta),
{:ok, proto} <- protocol_to_atom(protocol),
{:ok, mint_conn} <- Mint.HTTP.connect(proto, hostname, port, opts) do
Telemetry.stop(:connect, start_time, meta)
{:ok,
%Mojito.Conn{
conn: mint_conn,
protocol: proto,
hostname: hostname,
port: port
}}
end
end
defp protocol_to_atom("http"), do: {:ok, :http}
defp protocol_to_atom("https"), do: {:ok, :https}
defp protocol_to_atom(:http), do: {:ok, :http}
defp protocol_to_atom(:https), do: {:ok, :https}
defp protocol_to_atom(proto),
do: {:error, %Error{message: "bad protocol #{inspect(proto)}"}}
@doc ~S"""
Initiates a request on the given connection. Returns the updated Conn and
a reference to this request (which is required when receiving pipelined
responses).
"""
@spec request(t, Mojito.request()) :: {:ok, t, reference} | {:error, any}
def request(conn, request) do
max_body_size = request.opts[:max_body_size]
response = %Mojito.Response{body: [], size: max_body_size}
with {:ok, relative_url, auth_headers} <-
Utils.get_relative_url_and_auth_headers(request.url),
{:ok, mint_conn, request_ref} <-
Mint.HTTP.request(
conn.conn,
method_to_string(request.method),
relative_url,
auth_headers ++ request.headers,
:stream
),
{:ok, mint_conn, response} <-
stream_request_body(mint_conn, request_ref, response, request.body) do
{:ok, %{conn | conn: mint_conn}, request_ref, response}
end
end
defp stream_request_body(mint_conn, request_ref, response, nil) do
stream_request_body(mint_conn, request_ref, response, "")
end
defp stream_request_body(mint_conn, request_ref, response, "") do
with {:ok, mint_conn} <-
Mint.HTTP.stream_request_body(mint_conn, request_ref, :eof) do
{:ok, mint_conn, response}
end
end
defp stream_request_body(
%Mint.HTTP1{} = mint_conn,
request_ref,
response,
body
) do
{chunk, rest} = split_chunk(body, 65_535)
with {:ok, mint_conn} <-
Mint.HTTP.stream_request_body(mint_conn, request_ref, chunk) do
stream_request_body(mint_conn, request_ref, response, rest)
end
end
defp stream_request_body(
%Mint.HTTP2{} = mint_conn,
request_ref,
response,
body
) do
chunk_size =
min(
Mint.HTTP2.get_window_size(mint_conn, {:request, request_ref}),
Mint.HTTP2.get_window_size(mint_conn, :connection)
)
{chunk, rest} = split_chunk(body, chunk_size)
with {:ok, mint_conn} <-
Mint.HTTP.stream_request_body(mint_conn, request_ref, chunk) do
{mint_conn, response} =
if is_nil(rest) do
{mint_conn, response}
else
{:ok, mint_conn, resps} =
receive do
msg -> Mint.HTTP.stream(mint_conn, msg)
end
{:ok, response} = Mojito.Response.apply_resps(response, resps)
{mint_conn, response}
end
if response.complete do
{:ok, mint_conn, response}
else
stream_request_body(mint_conn, request_ref, response, rest)
end
end
end
defp method_to_string(m) when is_atom(m) do
m |> to_string |> String.upcase()
end
defp method_to_string(m) when is_binary(m) do
m |> String.upcase()
end
defp split_chunk(binary, chunk_size)
when is_binary(binary) and is_integer(chunk_size) do
case binary do
<<chunk::binary-size(chunk_size), rest::binary>> ->
{chunk, rest}
_ ->
{binary, nil}
end
end
end