Packages
pigeon
2.0.0-rc.3
2.0.1
2.0.0
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.1.0-rc.1
1.1.0-rc.0
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.13.1
0.13.0
0.12.1
0.12.0
0.11.0
0.10.3
0.10.2
0.10.1
0.10.0
0.9.1
0.9.0
0.8.0
0.7.0
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
iOS (APNS), Android (FCM), and Amazon Android (ADM) push notifications for Elixir.
Current section
Files
Jump to
Current section
Files
lib/pigeon/apns/config.ex
defmodule Pigeon.APNS.Config do
@moduledoc false
defstruct cert: nil,
key: nil,
port: 443,
ping_period: 600_000,
uri: nil
@typedoc ~S"""
Certificate APNS configuration struct
This struct should not be set directly. Instead use `new/1`
with `t:config_opts/0`.
## Examples
%Pigeon.APNS.Config{
cert: "certificate_content",
key: "key_content",
uri: "api.push.apple.com",
port: 443,
ping_period: 600_000
}
"""
@type t :: %__MODULE__{
cert: binary | nil,
key: binary | nil,
uri: binary | nil,
port: pos_integer,
ping_period: pos_integer
}
@typedoc ~S"""
Options for configuring certificate APNS connections.
## Configuration Options
- `:mode` - If set to `:dev` or `:prod`, will set the appropriate `:uri`
- `:cert` - Push certificate. Must be the full-text string of the file contents.
- `:key` - Push private key. Must be the full-text string of the file contents.
- `:uri` - Push server uri. If set, overrides uri defined by `:mode`.
Useful for test environments.
- `:port` - Push server port. Can be any value, but APNS only accepts
`443` and `2197`
- `:ping_period` - Interval between server pings. Necessary to keep long
running APNS connections alive. Defaults to 10 minutes.
"""
@type config_opts :: [
mode: :dev | :prod | nil,
cert: binary,
key: binary,
ping_period: pos_integer,
port: pos_integer,
uri: binary,
jwt_key: binary | {atom, binary},
jwt_key_identifier: binary | nil,
jwt_team_id: binary | nil
]
alias Pigeon.APNS.ConfigParser
@doc false
def default_name, do: :apns_default
@doc ~S"""
Returns a new `APNS.Config` with given `opts`.
## Examples
iex> Pigeon.APNS.Config.new(
...> mode: :prod,
...> cert: File.read!("test/support/FakeAPNSCert.pem"),
...> key: File.read!("test/support/FakeAPNSKey.pem"),
...> port: 2197,
...> ping_period: 300_000
...> )
%Pigeon.APNS.Config{
cert: "test/support/FakeAPNSCert.pem" |> File.read!() |> Pigeon.APNS.Config.decode_pem(),
key: "test/support/FakeAPNSKey.pem" |> File.read!() |> Pigeon.APNS.Config.decode_pem(),
ping_period: 300000,
port: 2197,
uri: "api.push.apple.com"
}
"""
def new(opts) when is_list(opts) do
%__MODULE__{
cert: opts |> Keyword.get(:cert) |> decode_pem(),
key: opts |> Keyword.get(:key) |> decode_pem(),
ping_period: Keyword.get(opts, :ping_period, 600_000),
port: Keyword.get(opts, :port, 443),
uri: Keyword.get(opts, :uri, ConfigParser.uri_for_mode(opts[:mode]))
}
end
@doc false
def decode_pem(bin) when is_binary(bin) do
case :public_key.pem_decode(bin) do
[{:Certificate, cert, _}] -> cert
[{:PrivateKeyInfo, key, _}] -> {:PrivateKeyInfo, key}
[{:RSAPrivateKey, key, _}] -> {:RSAPrivateKey, key}
_ -> {:error, {:invalid, bin}}
end
end
def decode_pem(other), do: {:error, {:invalid, other}}
end
defimpl Pigeon.Configurable, for: Pigeon.APNS.Config do
@moduledoc false
alias Pigeon.APNS.{
Config,
ConfigParser,
Shared
}
@type sock :: {:sslsocket, any, pid | {any, any}}
@type socket_opts :: maybe_improper_list(atom, integer | boolean)
# Configurable Callbacks
@spec connect(any) :: {:ok, sock} | {:error, String.t()}
def connect(%{uri: uri} = config) do
uri = to_charlist(uri)
options = connect_socket_options(config)
Pigeon.Http2.Client.default().connect(uri, :https, options)
end
defdelegate push_headers(config, notification, opts), to: Shared
defdelegate push_payload(config, notification, opts), to: Shared
defdelegate handle_end_stream(config, stream, notification), to: Shared
defdelegate schedule_ping(any), to: Shared
defdelegate close(config), to: Shared
@spec validate!(Config.t()) :: :ok | no_return
def validate!(%Config{cert: {:error, _}} = config) do
raise Pigeon.ConfigError,
reason: "attempted to start without valid certificate",
config: ConfigParser.redact(config)
end
def validate!(%Config{key: {:error, _}} = config) do
raise Pigeon.ConfigError,
reason: "attempted to start without valid key",
config: ConfigParser.redact(config)
end
def validate!(%Config{}) do
:ok
end
@spec connect_socket_options(Config.t()) :: socket_opts
def connect_socket_options(%{cert: cert, key: key} = config) do
[
{:cert, cert},
{:key, key},
{:password, ~c""},
{:packet, 0},
{:reuseaddr, true},
{:active, true},
:binary
]
|> Shared.add_port(config)
end
end