Inspired by the Rust eco-system and Hyper, HTTP headers are represented as type-safe plain objects. Instead of representing headers as a pair of
(key: string, value: value), the choice has been made to represent them as plain objects. This greatly reduces the risk of typo errors that can not catched by the compiler with plain old strings.
Instead, objects give the compiler the ability to catch errors directly at compile-time, as the user can not add or request a header through its name: it has to use the whole type. Types being enforced at compile-time, it helps reducing common typo errors.
With Pistache, each HTTP Header is a class that inherits from the
Http::Header base class and use the
NAME() macro to define the name of the header. List of all headers inside an HTTP request or response are stored inside an internal
std::unordered_map, wrapped in an
Header::Collection class. Invidual headers can be retrieved or added to this object through the whole type of the header:
get<H> will return a
H: Header (
H inherits from
Header). If the header does not exist,
get<H> will throw an exception.
tryGet<H> provides a non-throwing alternative that, instead, returns a null pointer.
Headers provided by Pistache live in the
Common headers defined by the HTTP RFC (RFC2616) are already implemented and available. However, some APIs might define extra headers that do not exist in Pistache. To support your own header types, you can define and register your own HTTP Header by first declaring a class that inherits the
Since every header has a name, the
NAME() macro must be used to name the header properly:
Http::Header base class provides two virtual methods that you must override in your own implementation:
This function is used to parse the header from the string representation. Alternatively, to avoid allocating memory for the string representation, a raw version can be used:
str will directly point to the header buffer from the raw http stream. The len parameter is the total length of the header's value.
When writing the response back to the client, the
write function is used to serialize the header into the network buffer.
Let’s combine these functions together to finalize the implementation of our previously declared header:
And that’s it. Now all we have to do is registering the header to the registry system:
You should always provide a default constructor for your header so that it can be instantiated by the registry system
XProtocolVersion can be retrieved and added like any other header in the
Headers that are not known to the registry system are stored as a raw pair of strings in the
getRaw() can be used to retrieve a raw header:
MIME Types (or Media Type) are also fully typed. Such types are for example used in an HTTP request or response to describe the data contained in the body of the message (
Content-Type header, …) and are composed of a type, subtype, and optional suffix and parameters.
MIME Types are represented by the
Mime::MediaType class, implemented in the
mime.h header. A MIME type can be directly constructed from a string:
However, to enforce type-safety, common types are all represented as enumerations:
To avoid such a typing pain, a
MIME macro is also provided:
For suffix MIMEs, use the special
If you like typing, you can also use the long form:
toString() function can be used to get the string representation of a given MIME type: