Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 1 addition & 9 deletions .dialyzer_ignore.exs
Original file line number Diff line number Diff line change
@@ -1,9 +1 @@
[
# The following four ignore rules are due to an upstream type problem in
# HTTPoison 3.0. Once https://github.com/edgurgel/httpoison/pull/511 has been
# merged and released, we should be able to delete these rules.
{"deps/httpoison/lib/httpoison/base.ex", :callback_arg_type_mismatch},
{"deps/httpoison/lib/httpoison/base.ex", :callback_type_mismatch},
{"deps/httpoison/lib/httpoison/base.ex", :pattern_match_cov},
{"lib/config_cat/api.ex", :invalid_contract}
]
[]
64 changes: 60 additions & 4 deletions lib/config_cat/api.ex
Original file line number Diff line number Diff line change
@@ -1,10 +1,66 @@
defmodule ConfigCat.API do
@moduledoc false
@type option ::
{:connect_timeout_milliseconds, non_neg_integer()}
| {:http_proxy, String.t() | nil}
| {:read_timeout_milliseconds, non_neg_integer()}

use HTTPoison.Base
@callback get(binary(), [{binary(), binary()}], [option]) :: {:ok, Req.Request.t()} | {:error, Exception.t()}
end

defmodule ConfigCat.API.ReqAPI do
@moduledoc false
@behaviour ConfigCat.API

alias Req.Request

@impl ConfigCat.API
def get(url, headers, options) do
req = build_request(url, headers, options)
Req.get(req)
end

@doc false
@spec build_request(binary(), [{binary(), binary()}], Keyword.t()) :: Req.Request.t()
def build_request(url, headers, options) do
options = Keyword.validate!(options, [:connect_timeout_milliseconds, :http_proxy, :read_timeout_milliseconds])
req = Req.new(decode_body: false, headers: headers, url: url)

Enum.reduce(options, req, &apply_option/2)
end

defp apply_option({:connect_timeout_milliseconds, timeout}, req) do
merge_connect_option(req, :timeout, timeout)
end

defp apply_option({:http_proxy, nil}, req), do: req

defp apply_option({:http_proxy, url}, req) do
uri = URI.parse(url)
proxy = {String.to_existing_atom(uri.scheme), uri.host, uri.port, []}

req
|> merge_connect_option(:proxy, proxy)
|> add_userinfo(uri.userinfo)
end

defp apply_option({:read_timeout_milliseconds, timeout}, req) do
Request.put_option(req, :receive_timeout, timeout)
end

defp add_userinfo(req, nil), do: req

defp add_userinfo(req, userinfo) do
proxy_headers = {"proxy-authorization", "Basic " <> Base.encode64(userinfo)}
merge_connect_option(req, :proxy_headers, proxy_headers)
end

defp merge_connect_option(req, key, value) do
new_connect_options =
req
|> Request.get_option(:connect_options, [])
|> Keyword.put(key, value)

@impl HTTPoison.Base
def process_request_headers(headers) do
[{"Accept", "application/json"} | headers]
Request.put_option(req, :connect_options, new_connect_options)
end
end
36 changes: 15 additions & 21 deletions lib/config_cat/config_fetcher.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ defmodule ConfigCat.ConfigFetcher do
@moduledoc false

alias ConfigCat.ConfigEntry
alias HTTPoison.Response

defmodule FetchError do
@moduledoc false
Expand Down Expand Up @@ -43,7 +42,7 @@ defmodule ConfigCat.CacheControlConfigFetcher do
alias ConfigCat.ConfigEntry
alias ConfigCat.ConfigFetcher
alias ConfigCat.ConfigFetcher.FetchError
alias HTTPoison.Response
alias Req.Response

require ConfigCat.ConfigCatLogger, as: ConfigCatLogger
require ConfigCat.Constants, as: Constants
Expand All @@ -54,7 +53,7 @@ defmodule ConfigCat.CacheControlConfigFetcher do
use TypedStruct

typedstruct enforce: true do
field :api, module(), default: ConfigCat.API
field :api, module(), default: ConfigCat.API.ReqAPI
field :base_url, String.t()
field :callers, [GenServer.from()], default: []
field :connect_timeout_milliseconds, non_neg_integer(), default: 8_000
Expand Down Expand Up @@ -209,26 +208,21 @@ defmodule ConfigCat.CacheControlConfigFetcher do
end

defp http_options(%State{} = state) do
options =
Map.take(state, [:http_proxy, :connect_timeout_milliseconds, :read_timeout_milliseconds])

Enum.map(options, fn
{:http_proxy, value} -> {:proxy, value}
{:connect_timeout_milliseconds, value} -> {:timeout, value}
{:read_timeout_milliseconds, value} -> {:recv_timeout, value}
end)
state
|> Map.take([:connect_timeout_milliseconds, :http_proxy, :read_timeout_milliseconds])
|> Keyword.new()
end

# This function is slightly complex, but still reasonably understandable.
# Breaking it up doesn't seem like it will help much.
# credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
defp handle_response(%Response{status_code: code, body: raw_config, headers: headers}, %State{} = state, etag)
defp handle_response(%Response{status: code, body: raw_config} = response, %State{} = state, etag)
when code >= 200 and code < 300 do
ConfigCatLogger.debug("ConfigCat configuration json fetch response code: #{code} Cached: #{extract_etag(headers)}")
ConfigCatLogger.debug("ConfigCat configuration json fetch response code: #{code} Cached: #{extract_etag(response)}")

with {:ok, decoded_config} <- Jason.decode(raw_config),
config = Config.inline_salt_and_segments(decoded_config),
new_etag = extract_etag(headers),
new_etag = extract_etag(response),
%{base_url: new_base_url, custom_endpoint?: custom_endpoint?, redirects: redirects} <-
state do
preferences = Config.preferences(config)
Expand Down Expand Up @@ -282,11 +276,11 @@ defmodule ConfigCat.CacheControlConfigFetcher do
end
end

defp handle_response(%Response{status_code: 304}, %State{} = state, _etag) do
defp handle_response(%Response{status: 304}, %State{} = state, _etag) do
{:ok, :unchanged, state}
end

defp handle_response(%Response{status_code: status} = response, %State{} = state, _etag) when status in [403, 404] do
defp handle_response(%Response{status: status} = response, %State{} = state, _etag) when status in [403, 404] do
ConfigCatLogger.error(
"Your SDK Key seems to be wrong. You can find the valid SDKKey at https://app.configcat.com/sdkkey. Received unexpected response: #{inspect(response)}",
event_id: 1100
Expand All @@ -308,7 +302,7 @@ defmodule ConfigCat.CacheControlConfigFetcher do
{:error, error, state}
end

defp handle_error({:error, %HTTPoison.Error{reason: :checkout_timeout} = error}, %State{} = state) do
defp handle_error({:error, %Req.TransportError{reason: :timeout} = error}, %State{} = state) do
ConfigCatLogger.error(
"Request timed out while trying to fetch config JSON. Timeout values: [connect: #{state.connect_timeout_milliseconds}ms, read: #{state.read_timeout_milliseconds}ms]",
event_id: 1102
Expand All @@ -329,10 +323,10 @@ defmodule ConfigCat.CacheControlConfigFetcher do
FetchError.exception(reason: error, transient?: true)
end

defp extract_etag(headers) do
case Enum.find(headers, fn {key, _value} -> String.downcase(key) == "etag" end) do
nil -> nil
{_key, value} -> value
defp extract_etag(%Response{} = response) do
case Response.get_header(response, "etag") do
[] -> nil
[etag] -> etag
end
end
end
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ defmodule ConfigCat.MixProject do
{:elixir_uuid, "~> 1.2"},
{:ex_doc, "~> 0.31.0", only: :dev, runtime: false},
{:excoveralls, "~> 0.18.0", only: :test},
{:httpoison, "~> 2.0 or ~> 3.0"},
{:jason, "~> 1.2"},
{:mix_test_interactive, "~> 5.1", only: :dev, runtime: false},
{:mox, "~> 1.1", only: :test},
{:req, "~> 0.6.2"},
{:styler, "~> 0.11", only: [:dev, :test], runtime: false},
{:typed_struct, "~> 0.3.0"},
{:tz, "~> 0.26.5", only: :test}
Expand Down
20 changes: 8 additions & 12 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
%{
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"certifi": {:hex, :certifi, "2.17.0", "835748414307e15e05b17d0e518190228ce648b08d569a5cc93a85a40f3e5c9b", [:rebar3], [], "hexpm", "8122798a17f0293c80daada25d0f81c7f4d708c73fef782c7c9b1950e26e4d21"},
"credo": {:hex, :credo, "1.7.19", "cc52129665fc7c15143d47838fda0f9cd6dac9ceced7bf4da6f85fcbfe64b12a", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "2d8bc95d5a7bb99dd2613621d4f08c6a3575c3fd4b62e6a2b48a100352a557b8"},
"dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"},
"earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"},
Expand All @@ -9,26 +8,23 @@
"ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"},
"excoveralls": {:hex, :excoveralls, "0.18.2", "86efd87a0676a3198ff50b8c77620ea2f445e7d414afa9ec6c4ba84c9f8bdcc2", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "230262c418f0de64077626a498bd4fdf1126d5c2559bb0e6b43deac3005225a4"},
"file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"},
"h2": {:hex, :h2, "0.10.2", "ea0146b9c8b5f3b5de16045765f5684db38ef1e66f1c60444890948cb1003e47", [:rebar3], [], "hexpm", "497a899f338b42e6a0b292524e635b0ce6f9379fa39395c8e38d06351cd9b9cf"},
"hackney": {:hex, :hackney, "4.4.3", "644c79b2eac605724e55fca651259c0560c4eea44b4c60b64baf5a2258b99376", [:rebar3], [{:certifi, "~> 2.17.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:h2, "~> 0.10.1", [hex: :h2, repo: "hexpm", optional: false]}, {:idna, "~> 7.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.2", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:quic, "~> 1.6.5", [hex: :quic, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:webtransport, "~> 0.4.1", [hex: :webtransport, repo: "hexpm", optional: false]}], "hexpm", "db6bbd96ff8562e6398c0f468ae63fc516857a0a12ecf232b3cb8e34fd167018"},
"httpoison": {:hex, :httpoison, "3.0.0", "8566a933bb9175236d1ec335978445b67cd1f5b5d3ead6ca4b80be469d41f5d9", [:mix], [{:hackney, "~> 4.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "9130197b7658901c493d6fcfb842fb9676300fa8a6c8ed058c8889cf1a77f3c2"},
"idna": {:hex, :idna, "7.1.0", "1067a13043538129602d2f2ce6899d8713125c7d19734aa557ce2e3ea55bd4f1", [:rebar3], [], "hexpm", "6ae959a025bf36df61a8cab8508d9654891b5426a84c44d82deaffd6ddf8c71f"},
"finch": {:hex, :finch, "0.23.0", "e3f9287ac25a8832f848b144c2b57346aac65b205e2e0629a52adfe6507fd837", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.8", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "80e58d3f936f57e3fdf404f83a3642897ae6d9fb642934e46da4d8fe761b99d5"},
"hpax": {:hex, :hpax, "1.0.4", "777de5d433b0fbdc7c418159c8055910faa8047ffdb3d6b31098d2a46cd7685c", [:mix], [], "hexpm", "afc7cb142ebcc2d01ce7816190b98ce5dd49e799111b24249f3443d730f377ca"},
"jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"},
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mimerl": {:hex, :mimerl, "1.5.0", "f35aca6f23242339b3666e0ac0702379e362b469d0aea167f6cc713547e777ed", [:rebar3], [], "hexpm", "db648ce065bae14ea84ca8b5dd123f42f49417cef693541110bf6f9e9be9ecc4"},
"mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"},
"mint": {:hex, :mint, "1.9.2", "6e89e698d69cc29be001afd02c2cf4bae2d0994efe69a2ced7aab440d71584f9", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "d8e952b432fdac2321f570d29a68b9eb2664dc80a7a2ef9464531a5425abf222"},
"mix_test_interactive": {:hex, :mix_test_interactive, "5.1.0", "a2edd66806f06f6a0dbb92f023126a7e4ecdf02f7d34e3bb16f76886b79bed43", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:process_tree, ">= 0.1.3", [hex: :process_tree, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "0df8ab74c176040acd78939e92a5328775bf9f414a0f0472372cce852ee548cf"},
"mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"parse_trans": {:hex, :parse_trans, "3.4.2", "c352ddc1a0d5e54f9b1654d45f9c432eef76f9cea371c55ddff769ef688fdb74", [:rebar3], [], "hexpm", "4c25347de3b7c35732d32e69ab43d1ceee0beae3f3b3ade1b59cbd3dd224d9ca"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"process_tree": {:hex, :process_tree, "0.3.0", "0eb58ec68f3d22a4f36040b0374469464c95fae5465c011b55bea01649b0bd70", [:mix], [], "hexpm", "6cb3b7be9c7d74b28a9f6e0f03115d5953e6bbda44be2b6fd9e667020870eb86"},
"quic": {:hex, :quic, "1.6.5", "28b7d49c1732b2de3861701bb03ae7798537240552370ac49f0d139d2ea8f621", [:rebar3], [], "hexpm", "de1a88972c33201a50d1a17c8c4a14528bd1d8f25ef705897d680ae312d0aa78"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
"req": {:hex, :req, "0.6.2", "b9b2024f35bcf60a92cc8cad2eaaf9d4e7aace463ff74be1afe5986830184413", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.21", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cc9cd30a2ddd04989929b887178e1610c940456d962c6c3a52df6146d2eef9bf"},
"styler": {:hex, :styler, "0.11.9", "2595393b94e660cd6e8b582876337cc50ff047d184ccbed42fdad2bfd5d78af5", [:mix], [], "hexpm", "8b7806ba1fdc94d0a75127c56875f91db89b75117fcc67572661010c13e1f259"},
"telemetry": {:hex, :telemetry, "1.4.2", "a0cb522801dffb1c49fe6e30561badffc7b6d0e180db1300df759faa22062855", [:rebar3], [], "hexpm", "928f6495066506077862c0d1646609eed891a4326bee3126ba54b60af61febb1"},
"typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"},
"tz": {:hex, :tz, "0.26.6", "4d46178dd5bc4d2c1e78c9affcc3fd46764e29cd2a148c06666edb83cb18629f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:mint, "~> 1.6", [hex: :mint, repo: "hexpm", optional: true]}], "hexpm", "9ca97ea48b412f2404740867f6c321ee8ce112602035bb79b0b90c9c03174652"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"},
"webtransport": {:hex, :webtransport, "0.4.1", "60ef6cd99282b5964c8172d674519239e31e357d934bde028bec70da34636fe5", [:rebar3], [{:h2, "~> 0.10.1", [hex: :h2, repo: "hexpm", optional: false]}, {:quic, "~> 1.6.5", [hex: :quic, repo: "hexpm", optional: false]}], "hexpm", "006e4e52a8f03b69201d4637c85b424a4ddccc1909f32c5742fed8d495a75174"},
}
Loading
Loading