Expand description
MLLP — the Minimal Lower Layer Protocol, which is how HL7 v2 messages actually cross a network.
A TCP stream is bytes without edges, and an HL7 v2 message carries no length prefix and no self-delimiting syntax, so a receiver reading a socket cannot tell where one message stops and the next begins. MLLP is the three-byte answer to that, and nothing more: wrap each message in a start block and an end block.
<VT> message <FS><CR>
0x0B 0x1C 0x0DThat is the whole protocol. It is deliberately minimal — no length, no
checksum, no session, no negotiation, no encryption — and everything
else people expect of a messaging layer is either HL7’s own
acknowledgement messages (ack), TLS underneath, or the caller’s
business.
§What is here
encode, decode | one frame in hand |
Framer | a byte stream, where frames arrive split across reads or several to a read — this is the one a socket needs |
Transport, IoTransport | frames over anything that reads and writes bytes |
ack | turning a received message into the acknowledgement HL7 expects back |
use hl7_2_mllp as mllp;
let message = "MSH|^~\\&|LAB|ACME|EHR|CLINIC|20260814080000||ORU^R01|99|P|2.5\rPID|1";
let frame = mllp::encode(message.as_bytes());
assert_eq!(frame[0], mllp::START_BLOCK);
assert_eq!(&frame[frame.len() - 2..], &[mllp::END_BLOCK, mllp::CARRIAGE_RETURN]);
assert_eq!(mllp::decode(&frame)?, message.as_bytes());§Reading a socket
use hl7_2_mllp::{IoTransport, Transport};
use std::net::TcpListener;
let listener = TcpListener::bind("127.0.0.1:2575")?;
for stream in listener.incoming() {
let mut transport = IoTransport::new(stream?);
while let Some(message) = transport.receive()? {
// ... process the message, then answer ...
}
}See examples/tcp_listener.rs for a complete server that also
acknowledges, and examples/tcp_sender.rs for the other end.
§Strictness
By default this crate is strict: a frame must start with <VT>, end
with <FS><CR>, and contain neither block character in between. Real
senders are not always strict — a missing <CR> after <FS>, and stray
bytes between frames, are the two common sins — so the noncompliance
feature relaxes exactly those two and nothing else.
It is off by default because a receiver that quietly accepts malformed framing is how a truncated message becomes a clinical record. Turn it on when you have a specific sender that needs it, and know which of the two you are forgiving.
spec/index.md in the repository is the normative specification of
everything above; where this documentation and that document disagree,
that document is right.
Re-exports§
Modules§
- ack
- Answering a message.
Structs§
- Framer
- Turns a stream of bytes into whole MLLP frames.
- IoTransport
- A
Transportover any byte stream:TcpStream, a TLS stream, a Unix socket, or aCursorin a test.
Enums§
Constants§
- CARRIAGE_
RETURN <CR>, the carriage return0x0Dthat followsEND_BLOCK.- DEFAULT_
LIMIT - The default cap on how much a
Framerbuffers while waiting for an end block: 16 MiB. - END_
BLOCK <FS>, the end block: a file separator,0x1C. Ends every frame, followed byCARRIAGE_RETURN.- START_
BLOCK <VT>, the start block: a vertical tab,0x0B. Begins every frame.
Traits§
- Transport
- Sending and receiving whole MLLP frames.
Functions§
- decode
- Unwrap one complete frame, returning the payload.
- decode_
with - Unwrap one complete frame at a chosen
Tolerance. - encode
- Wrap a payload in a frame:
<VT>+ payload +<FS><CR>. - is_
framable - Whether
payloadcan be framed unambiguously — that is, whether it is free of the two bytes MLLP reserves.