MLLP.Client (mllp v0.9.4)
MLLP.Client provides a simple tcp client for sending and receiving data via MLLP over TCP.
While MLLP is primarily used to send HL7 messages, MLLP.Client can be used to send non-hl7 messages, such as XML.
Connection Behaviour
Upon successful start up via start_link/4
, the client will attempt to establish a connection to the given address
on the provided port. If a connection can not be immediately established, the client will keep
trying to establish a connection per the value of :auto_reconnect_interval
which defaults to
1 second. Therefore it is possible that before a connection is fully established, the caller
may attempt to send a message which will result in MLLP.Client.Error.t()
being returned containing
the last error encountered in trying to establish a connection. Additionally, said behavour could be encountered
at any point during life span of an MLLP.Client process if the connection becomes severed on either side.
All connections, send, and receive failures will be logged as errors.
Examples
Sending messages as strings
iex> MLLP.Receiver.start(dispatcher: MLLP.EchoDispatcher, port: 4090)
{:ok,
%{
pid: #PID<0.2167.0>,
port: 4090,
receiver_id: #Reference<0.3312799297.2467299337.218126>
}}
iex> {:ok, client} = MLLP.Client.start_link("127.0.0.1", 4090)
{:ok, #PID<0.369.0>}
iex> msg = "MSH|^~\&|MegaReg|XYZHospC|SuperOE|XYZImgCtr|20060529090131-0500|..."
"MSH|^~\&|MegaReg|XYZHospC|SuperOE|XYZImgCtr|20060529090131-0500|..."
iex> MLLP.Client.send(client, msg)
{:ok, "MSH|^~\&|SuperOE|XYZImgCtr|MegaReg|XYZHospC|20060529090131-0500||ACK^A01^ACK|..."}
iex>
Sending messages with HL7.Message.t()
iex> MLLP.Receiver.start(dispatcher: MLLP.EchoDispatcher, port: 4090)
{:ok,
%{
pid: #PID<0.2167.0>,
port: 4090,
receiver_id: #Reference<0.3312799297.2467299337.218126>
}}
iex> {:ok, client} = MLLP.Client.start_link("127.0.0.1", 4090)
{:ok, #PID<0.369.0>}
iex> msg = HL7.Message.new(HL7.Examples.wikipedia_sample_hl7())
iex> MLLP.Client.send(client, msg)
{:ok, :application_accept,
%MLLP.Ack{
acknowledgement_code: "AA",
hl7_ack_message: nil,
text_message: "A real MLLP message dispatcher was not provided"
}}
Using TLS
iex> tls_opts = [
cacertfile: "/path/to/ca_certificate.pem",
verify: :verify_peer,
certfile: "/path/to/server_certificate.pem",
keyfile: "/path/to/private_key.pem"
]
iex> MLLP.Receiver.start(dispatcher: MLLP.EchoDispatcher, port: 4090, tls: tls_opts)
iex> {:ok, client} = MLLP.Client.start_link("localhost", 8154, tls: [verify: :verify_peer, cacertfile: "path/to/ca_certfile.pem"])
iex> msg = HL7.Message.new(HL7.Examples.wikipedia_sample_hl7())
iex> MLLP.Client.send(client, msg)
{:ok, :application_accept,
%MLLP.Ack{
acknowledgement_code: "AA",
hl7_ack_message: nil,
text_message: "A real MLLP message dispatcher was not provided"
}}
Link to this section Summary
Functions
Returns true if the connection is open and established, otherwise false.
Instructs the client to disconnect (if connected) and attempt a reconnect.
Sends a message and receives a response.
Sends a message without awaiting a response.
Starts a new MLLP.Client.
Stops an MLLP.Client given a MLLP.Client pid.
Link to this section Types
ip_address()
Specs
ip_address() :: :inet.socket_address() | String.t()
pid_ref()
Specs
Specs
t() :: %MLLP.Client{ address: ip_address(), auto_reconnect_interval: non_neg_integer(), backoff: any(), caller: pid() | nil, close_on_recv_error: boolean(), context: atom(), host_string: term(), last_byte_received: byte(), pid: pid() | nil, port: char(), receive_buffer: iolist(), send_opts: term(), socket: any(), socket_address: String.t(), socket_opts: Keyword.t(), tcp: module() | nil, tcp_error: term(), telemetry_module: module() | nil, tls_opts: Keyword.t() }
Link to this section Functions
connected(event, msg, data)
default_opts()
default_socket_opts()
disconnected(event, current_state, data)
fixed_socket_opts()
is_connected?(pid)
Specs
Returns true if the connection is open and established, otherwise false.
receive_impl(reply, data)
receiving(event_kind, request, data)
reconnect(pid)
Specs
reconnect(pid :: pid()) :: :ok
Instructs the client to disconnect (if connected) and attempt a reconnect.
send(pid, payload, options \\ %{}, timeout \\ :infinity)
Specs
send( pid :: pid(), payload :: HL7.Message.t() | String.t() | binary(), options :: MLLP.ClientContract.send_options(), timeout :: non_neg_integer() | :infinity ) :: {:ok, String.t()} | MLLP.Ack.ack_verification_result() | {:error, MLLP.ClientContract.client_error()}
Sends a message and receives a response.
send/4 supports both HL7.Message
and String.t().
All messages and responses will be wrapped and unwrapped via MLLP.Envelope.wrap_message/1
and
MLLP.Envelope.unwrap_message/1
respectively
In case the payload provided is an HL7.Message.t()
the acknowledgment returned from the server
will always be verified via MLLP.Ack.verify_ack_against_message/2
. This is the only case
where an MLLP.Ack.ack_verification_result()
will be returned.
Options
:reply_timeout
- Optionally specify a timeout value for receiving a response. Must be a positive integer or:infinity
. Defaults to 60 seconds.
send_async(pid, payload, timeout \\ :infinity)
Sends a message without awaiting a response.
Given the synchronous nature of MLLP/HL7 this function is mainly useful for testing purposes.
start_link(address, port, options \\ [])
Specs
start_link( address :: ip_address(), port :: :inet.port_number(), options :: MLLP.ClientContract.options() ) :: {:ok, pid()}
Starts a new MLLP.Client.
MLLP.Client.start_link/4 will start a new MLLP.Client process.
This function will raise a ArgumentError
if an invalid ip_address()
is provided.
Options
:use_backoff
- Specify if an exponential backoff should be used for connection. When an attempt to establish a connection fails, either post-init or at some point during the life span of the client, the backoff value will determine how often to retry a reconnection. Starts at 1 second and increases exponentially until reachingbackoff_max_seconds
seconds. Defaults totrue
.:backoff_max_seconds
- Specify the max limit of seconds the backoff reconection attempt should take, defauls to 180 (3 mins).:auto_reconnect_interval
- Specify the interval between connection attempts. Specifically, if an attempt to establish a connection fails, either post-init or at some point during the life span of the client, the value of this option shall determine how often to retry a reconnection. Defaults to 1000 milliseconds. This option will only be used ifuse_backoff
is set tofalse
.:reply_timeout
- Optionally specify a timeout value for receiving a response. Must be a positive integer or:infinity
. Defaults to 60 seconds.:socket_opts
- A list of socket options as supported by:gen_tcp
. Note that:binary
,:packet
, and:active
can not be overridden. Default options are enumerated below.- send_timeout: Defaults to 60 seconds
- send_timeout_close: Defaults to true
:close_on_recv_error
- A boolean value which dictates whether the client socket will be closed when an error in receiving a reply is encountered, this includes timeouts. Setting this totrue
is usually the safest behaviour to avoid a "dead lock" situation between a client and a server. This functions similarly to the:send_timeout
option provided by:gen_tcp
. Defaults totrue
.:tls
- A list of tls options as supported by:ssl
. When using TLS it is highly recommended you set:verify
to:verify_peer
, select a CA trust store using the:cacertfile
or:cacerts
options. Additionally, further hardening can be achieved through other ssl options such as enabling certificate revocation via the:crl_check
and:crl_cache
options and customization of enabled protocols and cipher suites for your specific use-case. See:ssl
for details.
stop(pid)
Specs
stop(pid :: pid()) :: :ok
Stops an MLLP.Client given a MLLP.Client pid.
This function will always return :ok
per :gen_statem.stop/1
, thus
you may give it a pid that references a client which is already stopped.