- Changed to be case-sensitive by default - Removed all references to the Go implementation - Clarified how append in a case-insensitive context should behave [ci skip] Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
6.5 KiB
4. nats-headers
Date: 2021-05-12
Status
Accepted
Context
This document describes NATS Headers from the perspective of clients. NATS headers allow clients to specify additional meta-data in the form of headers. The headers are effectively HTTP Headers.
The salient points of the HTTP header specification are:
- Each header field consists of a case-insensitive field name followed by a
colon (
:), optional leading whitespace, the field value, and optional trailing whitespace. - No spaces are allowed between the header field name and colon.
- Field value may be preceded or followed by optional whitespace.
- The specification may allow any number of strange things like comments/tokens etc.
- The keys can repeat.
More specifically from rfc822 Section 3.1.2:
Once a field has been unfolded, it may be viewed as being composed of a field-name followed by a colon (":"), followed by a field-body, and terminated by a carriage-return/line-feed. The field-name must be composed of printable ASCII characters (i.e., characters that have values between 33. and 126., decimal, except colon). The field-body may be composed of any ASCII characters, except CR or LF. (While CR and/or LF may be present in the actual text, they are removed by the action of unfolding the field.)
The only difference between a NATS header and HTTP is the first line. Instead of
an HTTP method followed by a resource, and the HTTP version (GET / HTTP/1.1),
NATS will provide a string identifying the header version (NATS/X.x),
currently 1.0, so it is rendered as NATS/1.0␍␊.
Please refer to the specification for information on how to encode/decode HTTP headers.
Enabling Message Headers
The server that is able to send and receive headers will specify so in it's
INFO protocol
message. The headers field if present, will have a boolean value. If the
client wishes to send headers, it has to enable it must add a headers field
with the true value in its
CONNECT message:
"lang": "node",
"version": "1.2.3",
"protocol": 1,
"headers": true,
...
Publishing Messages With A Header
Messages that include a header have a HPUB protocol:
HPUB SUBJECT REPLY 23 30␍␊NATS/1.0␍␊Header: X␍␊␍␊PAYLOAD␍␊
HPUB SUBJECT REPLY 23 23␍␊NATS/1.0␍␊Header: X␍␊␍␊␍␊
HPUB SUBJECT REPLY 48 55␍␊NATS/1.0␍␊Header1: X␍␊Header1: Y␍␊Header2: Z␍␊␍␊PAYLOAD␍␊
HPUB SUBJECT REPLY 48 48␍␊NATS/1.0␍␊Header1: X␍␊Header1: Y␍␊Header2: Z␍␊␍␊␍␊
HPUB <SUBJ> [REPLY] <HDR_LEN> <TOT_LEN>
<HEADER><PAYLOAD>
NOTES:
HDR_LENincludes the entire serialized header, from the start of the version string (NATS/1.0) up to and including the ␍␊ before the payloadTOT_LENthe payload length plus the HDR_LEN
MSG with Headers
Clients will see HMSG protocol lines for MSGs that contain headers
HMSG SUBJECT 1 REPLY 23 30␍␊NATS/1.0␍␊Header: X␍␊␍␊PAYLOAD␍␊
HMSG SUBJECT 1 REPLY 23 23␍␊NATS/1.0␍␊Header: X␍␊␍␊␍␊
HMSG SUBJECT 1 REPLY 48 55␍␊NATS/1.0␍␊Header1: X␍␊Header1: Y␍␊Header2: Z␍␊␍␊PAYLOAD␍␊
HMSG SUBJECT 1 REPLY 48 48␍␊NATS/1.0␍␊Header1: X␍␊Header1: Y␍␊Header2: Z␍␊␍␊␍␊
HMSG <SUBJECT> <SID> [REPLY] <HDR_LEN> <TOT_LEN>
<PAYLOAD>
HDR_LENincludes the entire serialized header, from the start of the version string (NATS/1.0) up to and including the ␍␊ before the payloadTOT_LENthe payload length plus the HDR_LEN
Decision
Implemented and merged to master.
Consequences
Use of headers is possible.
Compatibility Across NATS Clients
The following is a list of features to insure compatibility across NATS clients that support headers. Because the feature in Go client and nats-server leverage the Go implementation as described above, the API used will determine how header names are serialized.
Case-sensitive Operations
In order to promote compatibility across clients, this section describes how clients should behave. All operations are case-sensitive. Implementations should provide an option(s) to enable clients to work in a case-insensitive or format header names canonically.
Reading Values
GET and VALUES are case-sensitive operations.
GETreturns astringof the first value found matching the specified key in a case-sensitive lookup or an empty string.VALUESreturns a list of all values that case-sensitive match the specified key or an empty/nil/null list.
Setting Values
APPENDis a case-sensitive, and case-preserving operation. The header is set exactly as specified by the user.SETandDELETEare case-sensitive:DELETEremoves headers in case-sensitive operationSETcan be considered the result of aDELETEfollowed by anAPPEND. This means only exact-match keys are deleted, and the specified value is added under the specified key.
Case-insensitive Option
The operations GET, VALUES, SET, DELETE, APPEND in the presence of a
case-insensitive match requirement, will operate on equivalent matches.
This functionality is constrained as follows:
GETreturns the first matching header value in a case-insensitive match.VALUESreturns the union of all headers that case-insensitive match. If the exact key is not found, an empty/nil/null list is returned.DELETEremoves the all headers that case-insensitive match the specified key.SETis the combination of a case-insensitiveDELETEfollowed by anAPPEND.APPENDwill use the first matching key found and add values. If no key is found, values are added to a key preserving the specified case.
Note that case-insensitive operations are only suggested, and not required to be implemented by clients, specially if the implementation allows the user code to easily iterate over keys and values.
Multiple Header Values Serialization
When serializing, entries that have more than one value should be serialized one
per line. While the http Header standard, prefers values to be a comma separated
list, this introduces additional parsing requirements and ambiguity from client
code. HTTP itself doesn't implement this requirement on headers such as
Set-Cookie. Libraries, such as Go, do not interpret comma-separated values as
lists.