Releases: stephenberry/glaze
v6.5.1
Security Enhancements
BEVE/CBOR DoS Protection (#2194)
Binary formats like BEVE and CBOR encode length headers indicating how many elements follow. Previously, a malicious actor could craft a message claiming billions of elements but containing minimal data, causing memory exhaustion before validation.
Glaze now validates length headers against remaining buffer size before any memory allocation:
// Malicious buffer claiming 1 billion strings but containing only a few bytes
std::vector<std::string> result;
auto ec = glz::read_beve(result, malicious_buffer);
// ec.ec == glz::error_code::invalid_length
// No memory was allocated - attack preventedProtection applies to strings, typed arrays, generic arrays, and maps/objects.
User-Configurable Allocation Limits (#2195)
New compile-time options for stricter memory control:
struct secure_opts : glz::opts
{
uint32_t format = glz::BEVE;
size_t max_string_length = 1024; // Max 1KB per string
size_t max_array_size = 10000; // Max 10,000 elements per array
size_t max_map_size = 1000; // Max 1,000 entries per map
};
auto ec = glz::read<secure_opts{}>(obj, buffer);New glz::max_length wrapper for per-field limits:
template <>
struct glz::meta<UserInput>
{
using T = UserInput;
static constexpr auto value = object(
"username", glz::max_length<&T::username, 64>, // Max 64 chars
"scores", glz::max_length<&T::scores, 100> // Max 100 elements
);
};See Security Documentation for best practices.
New Features
Bounded Buffer Overflow Detection (#2189)
Writing to fixed-size buffers (like std::array or std::span) now returns error_code::buffer_overflow instead of undefined behavior when capacity is exceeded:
std::array<char, 32> buffer{};
auto ec = glz::write_json(large_object, buffer);
if (ec.ec == glz::error_code::buffer_overflow) {
// Handle insufficient buffer space
}allocate_raw_pointers Option (#2196)
New compile-time option to allow allocating memory for null raw pointers during deserialization:
struct alloc_opts : glz::opts {
bool allocate_raw_pointers = true;
};
std::vector<MyStruct*> vec;
auto ec = glz::read<alloc_opts{}>(vec, json);
// vec now contains allocated pointers - caller must delete themBy default, Glaze refuses to allocate raw pointers to prevent memory leaks. See Nullable Types for details.
Compatibility
iOS Support for Older Versions (#2197)
Added compatibility guards for std::to_chars/std::from_chars floating-point support, which is unavailable on iOS < 16.3. This affects only float128_t serialization; regular float and double types use Glaze's built-in implementation and work on all iOS versions.
Build
- Removed old Boost::system linkage (#2193)
Full Changelog: v6.5.0...v6.5.1
v6.5.0
Streaming I/O support, TOML datetime types, and binary size optimization options.
Streaming I/O
Glaze now supports streaming serialization and deserialization for processing large data with bounded memory usage. This enables reading/writing files of arbitrary size without loading everything into memory.
Output Streaming
Write directly to files or output streams with incremental flushing using basic_ostream_buffer:
#include "glaze/core/ostream_buffer.hpp"
std::ofstream file("output.json");
glz::basic_ostream_buffer<std::ofstream> buffer(file);
auto ec = glz::write_json(obj, buffer);
if (ec || !file.good()) {
// Handle error
}
// Or use the polymorphic alias for any std::ostream
glz::ostream_buffer<> buffer2(any_ostream);
glz::write_json(obj, buffer2);Input Streaming
Read directly from files or input streams with automatic refilling using basic_istream_buffer:
#include "glaze/core/istream_buffer.hpp"
std::ifstream file("input.json");
glz::basic_istream_buffer<std::ifstream> buffer(file);
my_struct obj;
auto ec = glz::read_json(obj, buffer);JSON/NDJSON Stream Reader
Process streams of JSON objects one at a time with json_stream_reader:
#include "glaze/json/json_stream.hpp"
struct Event {
int id;
std::string type;
};
std::ifstream file("events.ndjson");
glz::json_stream_reader<Event> reader(file);
Event event;
while (!reader.read_next(event)) {
process(event);
}
// Or use range-based for loop
for (auto&& event : glz::json_stream_reader<Event>(file)) {
process(event);
}TOML Datetime and Set Support
TOML now supports all four datetime formats from the TOML v1.1.0 specification, plus set-like containers. #2186
Supported datetime types:
| TOML Type | C++ Type |
|---|---|
| Offset Date-Time | std::chrono::system_clock::time_point |
| Local Date-Time | std::chrono::system_clock::time_point |
| Local Date | std::chrono::year_month_day |
| Local Time | std::chrono::hh_mm_ss<Duration> |
struct Config {
std::chrono::year_month_day date; // TOML: date = 2024-06-15
std::chrono::hh_mm_ss<std::chrono::milliseconds> time; // TOML: time = 10:30:45.123
std::chrono::system_clock::time_point timestamp; // TOML: timestamp = 2024-06-15T10:30:45Z
std::set<std::string> tags; // TOML: tags = ["a", "b", "c"]
};Binary Size Optimization Options
Two new options help reduce binary size and compilation time for embedded systems and size-constrained environments. #2188
linear_search Option
Uses linear key search instead of hash-based lookup for JSON object fields. This eliminates 256-byte hash tables per struct type, significantly reducing binary size. Faster for small structs (< ~8 fields) due to cache effects.
struct small_binary_opts : glz::opts
{
bool linear_search = true;
};
glz::read<small_binary_opts>(obj, json);glaze_DISABLE_ALWAYS_INLINE CMake Option
Disables aggressive inlining during compilation for smaller binaries and faster build times at the cost of runtime performance:
set(glaze_DISABLE_ALWAYS_INLINE ON)Full Changelog: v6.4.1...v6.5.0
v6.4.1
New formatting controls, TOML enum support, extended binary format validation, and SSL/TLS networking improvements.
Features
Float Formatting Control
New float_format option provides flexible control over floating-point precision in JSON output using C++23 std::format specifiers.
Global Option:
struct my_opts : glz::opts {
static constexpr std::string_view float_format = "{:.2f}";
};
double pi = 3.14159265358979;
glz::write<my_opts{}>(pi); // "3.14"Per-Member Wrapper:
template <>
struct glz::meta<my_type> {
using T = my_type;
static constexpr auto value = glz::object(
"lat", glz::float_format<&T::latitude, "{:.4f}">,
"lon", glz::float_format<&T::longitude, "{:.4f}">
);
};TOML Enum Support
Adds enum serialization and deserialization for TOML format, matching existing JSON enum functionality.
enum class Status { Pending, Active, Completed };
template <>
struct glz::meta<Status> {
using enum Status;
static constexpr auto value = glz::enumerate(Pending, Active, Completed);
};
Status s = Status::Active;
auto toml = glz::write_toml(s); // Returns: "Active"
Status parsed;
glz::read_toml(parsed, R"("Completed")"); // parsed == Status::Completederror_on_missing_keys for BEVE and MessagePack
The error_on_missing_keys option now works with BEVE and MessagePack formats, not just JSON. When enabled, deserialization fails with a missing_key error if required fields are absent. Optional/nullable fields (std::optional, std::unique_ptr) are still allowed to be missing.
SSL WebSocket and HTTPS Streaming Support
SSL/TLS support for WebSockets (WSS) and HTTPS streaming.
Key Features:
- WSS server support for secure WebSocket connections
- HTTPS streaming with
streaming_connection_interfacetype erasure websocket_connection_interfacefor socket-type agnostic handlers- SSL verify mode configuration for WebSocket clients
- ASIO 1.32+ compatibility fix for SSL streams
websocket_client client;
client.set_ssl_verify_mode(asio::ssl::verify_none); // For self-signed certs
client.connect("wss://localhost:8443/ws");Improvements
- API Consistency: Added
glz::read_beve_untaggedto matchwrite_beve_untaggednaming convention.read_binary_untaggedis now deprecated. #2178 GLIBCXX_USE_CXX11_ABI=0Support: Glaze can now be used and tested with the legacy GCC ABI. #2160- MessagePack Fuzz Testing: Added fuzz testing for MessagePack format. #2159
Bug Fixes
- Tagged Variant with Empty Structs: Fixed roundtrip failure for tagged variants containing empty struct types. #2180
- BEVE
std::array<bool, N>Compilation: Fixed constexpr compilation error when serializingstd::array<bool, N>with BEVE format. #2177 - Invalid Control Code Parsing: Fixed parsing of strings containing invalid control character sequences. #2169
- BEVE Skip Logic: Fixed bug where boolean and string typed arrays were handled incorrectly when skipping unknown keys in BEVE format. #2184
Full Changelog: v6.4.0...v6.4.1
v6.4.0
CBOR, MessagePack, generic_i64, generic_u64
This release adds support for CBOR and MessagePack along with enhanced runtime JSON manipulation capabilities and new generic JSON integer types.
New Formats
CBOR (Concise Binary Object Representation)
Glaze now provides comprehensive support for CBOR (RFC 8949). CBOR is an IETF standard that enables excellent interoperability with other languages and systems. #2145
#include "glaze/cbor.hpp"
my_struct s{};
std::string buffer{};
glz::write_cbor(s, buffer);
my_struct result{};
glz::read_cbor(result, buffer);Key Features:
- RFC 8949 compliance - Core CBOR specification support
- RFC 8746 typed arrays - Bulk memory operations for contiguous numeric containers (vectors, arrays)
- Multi-dimensional arrays - Row-major (tag 40) and column-major (tag 1040) support
- Eigen matrix support - Native serialization of fixed and dynamic Eigen matrices
- Complex numbers - IANA-registered tags (43000, 43001) for single and array complex types
- Floating-point preferred serialization - Automatically uses the smallest representation (half/single/double)
- Exceptions API -
glz::ex::write_cbor/glz::ex::read_cborfor exception-based error handling - Fuzz tested - Comprehensive fuzzing for robustness #2149
MessagePack
MessagePack support. #2015
#include "glaze/msgpack.hpp"
my_struct s{};
std::string buffer{};
glz::write_msgpack(s, buffer);
my_struct result{};
glz::read_msgpack(result, buffer);Key Features:
- Spec 2.0 compliance - Core types, extension types, and timestamp extension
- Timestamp extension - Type -1 per the MessagePack spec with all three formats (32, 64, 96 bit)
std::chrono::system_clock::time_pointintegrationglz::msgpack::ext- Direct handling of MessagePack extension values- Binary buffers - Compact
bin*tags forstd::vector<std::byte>and similar types - Partial read/write - JSON pointer support for selective serialization
- File helpers -
glz::write_file_msgpack/glz::read_file_msgpack - Options support - Works with standard Glaze options
To use the new formats, include the appropriate headers:
- CBOR:
#include "glaze/cbor.hpp" - MessagePack:
#include "glaze/msgpack.hpp"
Generic JSON Integer Types
New generic JSON types preserve integer precision beyond the 2^53 limit of double. #2057
| Type | Number Storage | Use Case |
|---|---|---|
glz::generic |
double |
Fast, JavaScript-compatible (default) |
glz::generic_i64 |
int64_t then double |
Signed integer precision up to 2^63-1 |
glz::generic_u64 |
uint64_t then int64_t then double |
Full unsigned 64-bit range |
glz::generic_u64 json{};
std::string buffer = R"({"big_id": 18446744073709551615})";
glz::read_json(json, buffer);
// Maximum uint64_t preserved exactly
assert(json["big_id"].get<uint64_t>() == 18446744073709551615ULL);Runtime JSON Manipulation
Runtime JSON Pointer Support
JSON pointer paths can now be defined at runtime. #2150
std::string buffer = R"({"action":"DELETE","data":{"x":10}})";
std::string path = "/action";
auto ec = glz::write_at(path, R"("GO!")", buffer);
// Result: {"action":"GO!","data":{"x":10}}Runtime Partial Write (write_json_partial)
Specify which fields to serialize at runtime using a whitelist approach. #2153
my_struct obj{};
std::vector<std::string> keys = {"name", "x"};
std::string buffer;
glz::write_json_partial(obj, keys, buffer);
// Only "name" and "x" fields are serializedFeatures:
- Output key order matches input container order
- Works with
std::vector<std::string>,std::vector<std::string_view>,std::array, etc. - Supports standard Glaze options like
prettify
Runtime Exclude Write (write_json_exclude)
Specify which fields to exclude at runtime using a blacklist approach. #2154
my_struct obj{};
std::vector<std::string> exclude = {"password", "internal_id"};
std::string buffer;
glz::write_json_exclude(obj, exclude, buffer);
// All fields except "password" and "internal_id" are serializedNetworking Improvements
Templated HTTP Router
basic_http_router is now templated for custom handler types. #2151
// Use custom handler types with the HTTP router
glz::basic_http_router<MyCustomHandler> router;Additional Improvements
rawoption support fortime_point- Serialize time points as raw integer values #2147- Documentation improvements - Added
simple_enumcallout and updated documentation website links
Fixes
- Fixed
rawandraw_stringcombined options - Correct behavior when both options are specified #2148 - MSVC compatibility fix - Resolved build issues on MSVC #2158
Full Changelog: v6.3.0...v6.4.0
v6.3.0
Major Features
- RFC compliance for JSON Patch and JSON Merge Patch
- Big endian architecture support
std::chronoJSON support- Enhanced BEVE format streaming capabilities
Big Endian Support
Full support for big endian architectures with compile-time detection via std::endian::native. Includes byte-swapping implementations across all components, with CI testing through GCC cross-compilation and QEMU. (Closes #1675)
JSON Patch (RFC 6902)
Complete implementation of RFC 6902 JSON Patch for computing and applying document differences:
glz::diff()- Generate patches between documentsglz::patch()- Apply operations in-placeglz::patched()- Non-mutating patch applicationglz::patch_json()- Work directly with JSON strings- All six operations:
add,remove,replace,move,copy,test - Atomic transactions with automatic rollback on failure
- Option to create missing intermediate paths
- #2131
JSON Merge Patch (RFC 7386)
Full support for RFC 7386 JSON Merge Patch:
merge_patch()- Apply patches in-placemerge_patched()- Returns new patched valuemerge_diff()- Generate patches between documents- Correct handling of null-value removal, recursive merging, and array replacement
- #2132
std::chrono Support
Zero-allocation serialization for chrono types: (Closes #1110)
std::chrono::duration- Serialized as numeric countstd::chrono::system_clock::time_point- ISO 8601 UTC formatstd::chrono::steady_clock::time_point- Numeric format- Epoch wrappers:
epoch_seconds,epoch_millis,epoch_micros,epoch_nanos - Precision-aware fractional seconds based on duration type
- Automatic timezone offset conversion to UTC
- #2130
BEVE Delimiter and Incremental Writing
New streaming capabilities for BEVE (Binary Efficient Value Encoding) similar to NDJSON for JSON:
write_beve_delimiter()- Outputs delimiter byte (0x06)write_beve_append()- Adds values without clearing bufferwrite_beve_append_with_delimiter()- Streaming-friendly writeswrite_beve_delimited()- Container serialization with delimitersread_beve_delimited()- Read delimiter-separated valuesread_beve_at()- Offset-based reading with automatic delimiter skipping
When converting delimited BEVE to JSON via beve_to_json, delimiters become newlines for NDJSON-compatible output.
std::u8string and std::u8string_view Support
Native serialization support for UTF-8 string types.
mimic Type in glz::meta
New mimic type alias to prevent double-quoting of custom types as map keys: (Closes #1477)
template <>
struct glz::meta<my_key> {
using mimic = std::string;
static constexpr auto value = &my_key::value;
};Networking Improvements
- HTTP Server Keep Alive - Persistent connection support for HTTP servers #2128
- WebSocket internal() Methods - Access underlying implementation via
internal()andsocket()methods #2140
Performance Improvements
- Reduced template instantiations when various
glz::optsare used, decreasing compile times #2138 - Pass end iterator by value for higher performance in parsing #2133
- Pass
std::string_viewby value throughout codebase #2134 - Remove
reinterpret_castfrom atoi implementation #2136
Code Cleanup
- Removed legacy
hash_mapimplementation and cleaned up variant hashing #2135
Bug Fixes
- Fixed
glz::seekfor invalid paths to maps by usingfind#2129
Breaking Changes
- Error code renamed:
get_nonexistent_json_ptr→nonexistent_json_ptrfor consistency #2131
Full Changelog: v6.2.0...v6.3.0
v6.2.0
v6.2.0
Major networking and RPC enhancements, including JSON RPC 2.0 registry support, zero-copy REPE handling, and a standardized ABI-stable plugin interface for REPE.
Breaking Changes
-
Move
append_arraysanderror_on_const_readoptions out ofglz::optsby @stephenberry in #2110- These options are now inheritable options that must be added to a custom options struct.
- See How to Use Inheritable Options
-
WebSocket Client:
ctx_changed tocontext()method by @stephenberry in #2106// Before (won't compile) client.ctx_->stop(); // After client.context()->stop();
Highlights
JSON RPC 2.0 Registry Support
Added support for JSON RPC 2.0 protocol to the registry, enabling standard JSON-based remote procedure calls alongside REPE.
struct my_api
{
int counter = 0;
std::string greet() { return "Hello, World!"; }
int add(int value) { counter += value; return counter; }
};
// Create a JSON-RPC registry
glz::registry<glz::opts{}, glz::JSONRPC> server{};
my_api api{};
server.on(api);
// Call a function
auto response = server.call(R"({"jsonrpc":"2.0","method":"greet","id":1})");
// Returns: {"jsonrpc":"2.0","result":"Hello, World!","id":1}
// Read a variable
response = server.call(R"({"jsonrpc":"2.0","method":"counter","id":2})");
// Returns: {"jsonrpc":"2.0","result":0,"id":2}
// Call a function with parameters
response = server.call(R"({"jsonrpc":"2.0","method":"add","params":10,"id":3})");
// Returns: {"jsonrpc":"2.0","result":10,"id":3}REPE Plugin Interface
A new standardized plugin interface for REPE enables ABI-stable dynamic plugin systems that work seamlessly with glz::registry and glz::asio_server. This includes:
- Pure C header (
plugin.h) for cross-compiler compatibility - C++ helper (
plugin_helper.hpp) for implementing plugins usingglz::registry - Interface versioning for safe plugin loading
extern "C" {
uint32_t repe_plugin_interface_version() { return REPE_PLUGIN_INTERFACE_VERSION; }
const char* repe_plugin_name() { return "calculator"; }
repe_buffer repe_plugin_call(const char* request, uint64_t request_size) {
return glz::repe::plugin_call(registry, request, request_size);
}
}Zero-Copy REPE Handling
True zero-copy implementation for the REPE RPC protocol, eliminating unnecessary memory allocations and copies in the hot path:
parse_requestreturns views into the original bufferresponse_builderwrites directly to output buffer
server.call = [](std::span<const char> request, std::string& response_buffer) {
auto result = glz::repe::parse_request(request);
// req.query and req.body are views into request
registry.call(request, response_buffer); // Zero-copy
};Indexed rename_key API
A new indexed rename_key API allows transforming JSON keys based on member type information at compile time. This is useful for automatically using enum type names as JSON keys:
template <>
struct glz::meta<AppContext> {
template <size_t Index>
static constexpr auto rename_key() {
using MemberType = glz::member_type_t<AppContext, Index>;
if constexpr (std::is_enum_v<MemberType>) {
return glz::name_v<MemberType>;
} else {
return glz::member_nameof<Index, AppContext>;
}
}
};Output: {"num":42,"MyEnum":"Second","MyFlag":"Yes"}
New Compile Time Options
skip_null_members_on_read- Skip null values when reading, preserving existing values by @stephenberry in #2086skip_self_constraint- Disable self constraint validation via a compile time option by @stephenberry in #2121
Improvements
- asio_server custom call handler for routing and middleware by @stephenberry in #2096
glz::mergesupport for the registry by @stephenberry in #2088- REPE buffer helpers by @stephenberry in #2095
- Simplify REPE plugin initialization by @stephenberry in #2119
- CMake improvements by @stephenberry in #2112
- Use
weak_ptrrather thanshared_ptrfor server lifetime safety by @stephenberry in #2113 - Extract
decode_index_unknown_keyby @stephenberry in #2111 std::spanserialization tests by @stephenberry in #2109- Better client socket connection handling by @stephenberry in #2105
- Safer
unique_socket, delete copy constructor and assignment by @stephenberry in #2104 - Improve asio client
connected()status by @stephenberry in #2103 repe_plugin_datastruct by @stephenberry in #2099
Fixes
- WebSocket Client: Fix shared context and improve lifetime safety by @stephenberry in #2106
- Fix concurrent WebSocket connections (thread-safe write queue, RFC 6455 close handshake) by @stephenberry in #2092
- Fix MSVC with
std::uniform_int_distribution<unsigned int>by @stephenberry in #2085 - Fix GCC warnings in tests by @stephenberry in #2094
- Fix Clang build warnings for unit tests and bump asio dependency versions by @stephenberry in #2107
- Don't export
-Wno-missing-bracesfor languages other than C and C++ by @stephenberry in #2076 - Cleanup broken tests by @stephenberry in #2118
Testing
- Test
error_on_missing_keyswith JSON schema generation by @stephenberry in #2093 - More registry tests by @stephenberry in #2087
Documentation
Extensive documentation added:
- ASIO Setup Guide
- REPE Plugin Interface
- REPE Buffer API
- JSON-RPC Registry
- Rename Keys
- Compile Time Options
- WebSocket client documentation updates
Full Changelog: v6.1.0...v6.2.0
v6.1.0
Lots of improvements as I've been working through the active Glaze issues.
Breaking Changes
- Remove bools_as_numbers from glz::opts by @stephenberry in #2008
This option is still available, it is just no longer a coreglz::optsoption, to reduce the size of compilation errors.
Improvements
- Wrapping Middleware by @stephenberry in #2044
- Support for requires_key with JSON reading by @stephenberry in #2061
- Support for move only types in std::variant and broader variant id support by @stephenberry in #2064
- Allow http_server to run on external io_context. by @gobdevel in #2063
- Better constructors for glz::generic by @stephenberry in #2067
- Websocket Client by @stephenberry in #2068
- validate_utf8 SWAR by @stephenberry in #2074
- improve asio_server error handling and keep-alive by @stephenberry in #2070
- Feature/http server port by @stephenberry in #2078
- Add glz::escape_bytes_t by @stephenberry in #2079
- JMESPath slicing support for tuples by @stephenberry in #2081
- Make get_enum_name work with non-monotonic enums by @stephenberry in #2082
Fixes
- websocket client fixes by @stephenberry in #2071
- Fix request_line UB by @stephenberry in #2075
Testing
- variant int/float deduction tests by @stephenberry in #2058
- Nullable lambda tests by @stephenberry in #2059
- Demonstrate glz::custom with optional types through glz::generic by @stephenberry in #2080
New Contributors
Full Changelog: v6.0.3...v6.1.0
v6.0.3
Improvements
- glz::get for glz::generic and containers by @stephenberry in #2052
This change also adds much fasterglz::genericsupport reading into compatible types - BEVE skip null members support by @stephenberry in #2054
Fixes
- Fix BEVE serialization for empty arrays by @stephenberry in #2041
- const Eigen support by @stephenberry in #2042
- Prevent Clang missing braces warnings (#2024 updated) by @pps83 in #2043
- Do not use msvc-specific pragmas with clang-based compilers by @pps83 in #2050
- Disable msvc warning for unary minus operator applied to unsigned type by @pps83 in #2049
- MSVC fix by @stephenberry in #2051
- safe_resizable_buffer concept for output_buffer by @stephenberry in #2056
- Fix MSVC warnings by @stephenberry in #2055
Full Changelog: v6.0.2...v6.0.3
v6.0.2
New Features
skip_ifruntime value skipping in #2029
struct user_settings_t {
std::string theme = "light";
int volume = 50;
};
template <>
struct glz::meta<user_settings_t> {
template <class T>
static constexpr bool skip_if(T&& value, std::string_view key, const
glz::meta_context&) {
using V = std::decay_t<T>;
if constexpr (std::same_as<V, std::string>) {
return key == "theme" && value == "light";
}
else if constexpr (std::same_as<V, int>) {
return key == "volume" && value == 50;
}
return false;
}
};- REPE to/from JSON RPC 2.0 in #2026
Improvements
Fixes
- Fix websocket closing code and make it more robust by @stephenberry in #2020
- Fix a typo of a field declaration in unknown-keys.md by @ivanka2012 in #2017
- Fix integer UB by @stephenberry in #2023
- Write member functions when
write_member_functions = trueby @stephenberry in #2028
Full Changelog: v6.0.1...v6.0.2
v6.0.1
Improvements
- Strong ID and deeper cast support for use with map keys with BEVE by @stephenberry in #2002
- Improve parsing for TOML integers by @friedkeenan in #2007
- Better TOML skipping and thus unknown key handling by @stephenberry in #2013
Fixes
- Checking for Opts.comments when skipping ws to fix JSONC parse issue by @stephenberry in #2005
- Fixed read_constraint optional handling with missing-key option by @stephenberry in #2010
Full Changelog: v6.0.0...v6.0.1