github stephenberry/glaze v7.6.0

latest releases: v9.0.0, v8.4.0, v8.3.0...
4 months ago

Unified HTTP Routing

Streaming and WebSocket routes now live in basic_http_router alongside normal routes, sharing the radix-tree matcher. As a result, streaming and WebSocket routes support :param and *wildcard segments, and any mix of route kinds can be passed to server.mount().

glz::http_router router;
router.get("/users/:id/profile", normal_handler);
router.stream_get("/users/:id/events", streaming_handler);
router.websocket("/rooms/:room/ws", ws_server);

glz::http_server server;
server.mount("/api", router);

New router methods: stream, stream_get, stream_post, websocket, match_streaming, match_websocket. The flat streaming_handlers_ / websocket_handlers_ maps on http_server are gone; mount() now propagates all three route kinds.

Behavior change: streaming routes are matched before normal routes, so a streaming /items/:id will intercept requests that previously fell through to a normal /items/42. Documented in docs/networking/http-router.md.

Migration: the public router.routes field is now router.normal_routes.routes; a routes() accessor is provided for source compatibility.

  • Unify streaming/WebSocket routes into http_router with path params by @stephenberry in #2559

HTTP Client Connection Pooling

glz::http_client now keeps reused keep-alive connections healthy and transparently retries lost ones. Three coordinated changes:

  • Pool eviction. Pooled entries carry a return timestamp; anything older than idle_timeout is evicted on the next acquire() (default 4s, sized just under uvicorn's 5s). Plain TCP sockets get an active non-blocking MSG_PEEK to detect FIN/RST before any request bytes go on the wire. SSL sockets rely on timestamp eviction plus the retry path.
  • Transparent retry on idempotent methods, gated on the precise invariant: the server has not yet started sending a response on this attempt. Retries fire only when the failure is a connection-level error (EOF, ECONNRESET, EPIPE, ECONNABORTED, etc.), no response bytes have been received yet, and the method is idempotent. POST/PATCH are never auto-retried.
  • Pre-built request bytes. Wire bytes are built once at the top of perform_request_async / perform_sync_request and threaded through the chain as shared_ptr<std::string>, so internal lambda captures are one refcount bump instead of a std::string + header-map copy.

New API:

void http_client::set_pool_idle_timeout(std::chrono::steady_clock::duration);
void http_client::set_pool_max_connections_per_host(size_t);
void http_client::set_pool_active_liveness_check(bool);
void http_client::clear_connection_pool();

Defaults match common HTTP clients (10 connections per host, 4s idle timeout). Existing API is unchanged.

  • http_client: pool eviction, transparent retry, request-bytes sharing by @stephenberry in #2551

Type-Level Skip via glz::skip{}

A type can now opt out of serialization entirely by setting its meta value to glz::skip{}:

// local meta
struct Marker { struct glaze { static constexpr auto value = glz::skip{}; }; };

// external meta
template <> struct glz::meta<Marker> { static constexpr auto value = glz::skip{}; };

All format writers now key off a unified always_skipped concept.

Wire-format change for glz::file_include: BEVE / MsgPack / CBOR / BSON / JSONB previously emitted file_include fields as a placeholder. They are now filtered, matching JSON's longstanding behavior. Older blobs containing such fields will not deserialize with the new reader. JSON / TOML / YAML are unaffected.

C++26 std::inplace_vector Compatibility

has_try_emplace_back no longer hard-requires T*; it accepts any result that is contextually convertible to bool, so a real C++26 std::inplace_vector (whose try_emplace_back returns std::optional<T&> per P3981) is detected. JSON parser call sites use truthy / not checks instead of != nullptr / == nullptr.

glz::inplace_vector mirrors the matching API shape under GLZ_HAS_OPTIONAL_REF, auto-detected via __cpp_lib_optional > 202110L. On stdlibs that ship std::optional<T&> the polyfill returns optional<T&>; otherwise it keeps the C++23 T* shape. Users can force the path with -DGLZ_HAS_OPTIONAL_REF=1.

  • inplace_vector: match C++26 P3981 try_emplace_back / try_push_back API by @stephenberry in #2552

Fixes

  • Streaming and WebSocket routes with query strings: http_server::stream_* handlers (and the WebSocket upgrade path) failed to match any request that included a query string. Lookups now key off request.path after parsing, so streaming and WebSocket handlers see request.path and request.query populated the same way regular handlers do by @stephenberry in #2550
  • Variant deduction for vector<pair<...>> as JSON object: a std::variant containing a vector<pair<...>> alternative no longer fails with no_matching_variant_type on {...} JSON input. Variant deduction is now option-aware via check_concatenate(Opts), matching the direct parser's concatenate-mode behavior by @stephenberry in #2533
  • ordered_small_map parse error under namespace pollution: parsing no longer fails when std::hash is hoisted to global scope by @stephenberry in #2535
  • EETF refinements: noexcept added to EETF functions; eetf_to_json now uses eetf_opts; requires clause restricts input buffers to single-byte element types (required by ei functions); fixed universal-reference forwarding; added a test for eetf_to_json conversion without an EETF header by @fregate in #2547
  • MSVC 14.50 warnings in zmij.hpp: suppress C4702 (likely a compiler regression) and C4324 (intentional for SIMD). No behavior change by @JnCrMx in #2556
  • GCC 16 delegate constructor warning/error by @JnCrMx in #2548
  • Typo fixes by @JnCrMx in #2537

CI

  • Consolidate the standalone P2996 reflection workflow into gcc.yml as a build-gcc16-reflection job on the official gcc:16.1 Docker image by @stephenberry in #2553
  • Update GCC C++26 Reflection CI by @stephenberry in #2538

Full Changelog: v7.5.0...v7.5.1

Don't miss a new glaze release

NewReleases is sending notifications on new releases.