Skip to content

feat(cpp-boost-beast-server): add HTTP/1.1 server generator - #24783

Open
bold84 wants to merge 18 commits into
OpenAPITools:masterfrom
bold84:pr/cpp-boost-beast-server
Open

feat(cpp-boost-beast-server): add HTTP/1.1 server generator#24783
bold84 wants to merge 18 commits into
OpenAPITools:masterfrom
bold84:pr/cpp-boost-beast-server

Conversation

@bold84

@bold84 bold84 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

cpp-boost-beast-server: new OpenAPI HTTP/1.1 server generator (C++17)

Builds directly on current master, which includes the client OAS 3.1 work from #24760 (squash-merged as c27ee27572b). The diff contains only the server additions — 13 commits, 103 files (+16,435/−1,447): ~7,200 lines of generator source + tests, ~9,200 lines of committed sample/docs (the usual shape for a new-generator PR in this repo).

Adds a new cpp-boost-beast-server generator that produces a C++17 Boost.Beast HTTP/1.1 server from OpenAPI documents (OAS 3.0 and 3.1). It shares the model pipeline with the client generator (refactored into CppBoostBeastModelCodegen without changing client output) and reuses the OAS 3.1 schema-validation runtime for request-body validation.

Scope

Generator

  • New cpp-boost-beast-server generator (beta) with typed request/responder contracts per operation (GetPetByIdRequest, GetPetByIdResponder), OAS parameter deserialization (path/query/header/cookie, incl. simple/label/matrix/form/spaceDelimited/pipeDelimited/deepObject-string-map styles), and a security seam (Authorizer, deny-by-default).
  • Shares the model pipeline with the client via CppBoostBeastModelCodegen; client output stays byte-identical.
  • OAS 3.1 request bodies are validated with the same generated schema-evaluator runtime the client uses; OAS 3.0 continues through the normal path.
  • Hard-rejects at generation time only what cannot route deterministically: ambiguous path templates (/a/{x}/c vs /a/{y}/c) and ranged status codes (2XX). Fails closed with precise diagnostics.
  • Degrades with a warning everything else the runtime cannot serve faithfully — chosen so real-world corpora (canonical petstore, the OpenAI spec) still generate compiling code, per the repository harness contract:
    • request media types are filtered to JSON (a mixed body accepts JSON and answers 415 to the rest; a JSON-less body loses its typed body);
    • response media types serialize as JSON;
    • security scheme types with no runtime credential extractor (oauth2/openIdConnect/mutualTLS) deny all requests with 401 rather than being silently unauthenticated;
    • parameters the codecs cannot decode from a raw string are dropped from the handler (content-style, form fields, non-deepObject object queries, non-scalar array items, cookie containers, unsupported styles, heterogeneous enums, enum-class dataTypes);
    • request bodies whose model resolves to std::variant (composition unions, including models aliased to one) get no typed body, since decode-side union matching needs the schema matcher the request path does not run.
  • Recovery: a multipart+JSON body whose JSON member $refs a generated model types the handler body exactly (its #include is appended via toModelImport); a body model colliding with the generated request struct is namespace-qualified.
  • RFC 9457 application/problem+json errors throughout, with WWW-Authenticate challenges for http-scheme security.

Runtime

  • Boost 1.81+ floor; Boost.URL as a compiled library; strand-based HttpServer with keep-alive, request version/keep-alive mirroring, 413 body-limit handling that closes the connection, and atomic single-completion guard on responders.
  • Router ranks literal path segments over parameters, so /pets/bulk wins over an earlier-registered /pets/{petId}.
  • Deferred handler completions are supported: the response-write deadline is re-armed per response, so a worker-thread reply arriving after the read timeout still reaches the client.
  • Anonymous security alternatives (security: [] or an empty OR-group) bypass the authorization gate with no authorizer required.
  • Generated C++ builds clean under -Wall.

Sample, docs, CI

  • samples/server/petstore/cpp-boost-beast-server (byte-deterministic regeneration).
  • docs/generators/cpp-boost-beast-server.md and generator index entry.
  • .github/workflows/samples-cpp-boost-beast-server.yaml: Ubuntu / macOS / Windows build matrix with per-OS Boost installs (Windows: vcpkg boost-asio/boost-beast/boost-json/boost-url/boost-multiprecision).

Verification

  • Real-world corpus: the full OpenAI OpenAPI document (~1 MB, OAS 3.1, 4,871 model files, 74 API files) generates, compiles with -Wall -Wextra at 0 errors / 0 warnings, and links — exercising the degrade policy (form fields, variant-alias and mixed bodies, enum-class params) and the recovery path (recovered JSON-member bodies with appended includes) at scale.
  • Repository contract: AllGeneratorsTest passes, i.e. this generator accepts src/test/resources/3_0/petstore.yaml (the spec that mixes XML/form/multipart payloads and an oauth2 scheme) exactly as the harness requires — degrade, not reject.
  • Focused Boost.Beast selection on this head: 184 tests, 0 failures, 5 skips (generated-C++ runtime tests skip when Boost headers are unavailable on the runner).
  • Real-world corpus: the full OpenAI OpenAPI document (~1 MB, OAS 3.1, 4,871 model files, 74 API files) generates, compiles with the generated project's -Wall build at 0 errors / 0 warnings, and links — exercising the degrade policy (form fields, variant-alias and mixed bodies, enum-class params) and the recovery path (recovered JSON-member bodies with appended includes) at scale.
  • Petstore sample builds warning-free and smoke-tested (501 stubs, 400 bad path param, 404 unknown route).
  • Sample regeneration is byte-deterministic; client sample unchanged by the shared-pipeline refactor.

Notes

  • Kept as draft pending maintainer bandwidth; the feat(cpp-boost-beast): add OAS 3.1 schema validation #24760 dependency is resolved (merged), so the diff is final and review-ready.
  • The generator is marked beta; the runtime is designed for embedding (all generated sources in the project), not yet published as a standalone library.

bold84 added 7 commits August 27, 2026 01:42
…rver

Move the direction-agnostic document pipeline, model lowering, parameter
serialization facts, dialect policy, and schema-IR emission from
CppBoostBeastClientCodegen into CppBoostBeastModelCodegen; extract
CppBoostBeastOperationFacts from the client template assembler; add
additionalEmbeddedTemplateDirs locator support and move shared
model/validation templates to cpp-boost-beast-common. Client generated
output is byte-identical; cppboostbeast suite (158 tests) green.
Add the cpp-boost-beast-server generator (BETA) emitting a C++17
Boost.Beast HTTP/1.1 server: strand-per-connection sessions with
message_generator responses, encoded-segment routing with 404/405+Allow,
OAS parameter deserialization (path simple/label/matrix, query
form/space/pipe/deepObject, header simple, cookie form) with enum,
pattern, and bound validation into RFC 9457 problem responses, JSON body
codec over generated model to/fromJsonValue APIs, OR-of-AND security
extraction with a deny-by-default Authorizer seam, single-shot strand
posting responders, and an addApiImplStubs quick-start main. CMake links
Boost 1.81+ json+url compiled libraries; -Wall/-W4 clean.
…e suites

Add the server-regression OAS 3.1 fixture (path/query/header/cookie
styles, enum/pattern/bound constraints, bearer + apiKey security),
12-test codegen suite (defaults, contract emission, stubs, IR stripping,
multipart/x-www-form-urlencoded/text-*/event-stream/content-style/
cookie-matrix/ambiguous-route rejections, 3.0 compatibility), and the
native loopback runtime test compiling and running the generated server
against real sockets: 200/201/204 happy paths, 400 problem+json with
errors[] for every constraint class, 404, 405+Allow, 413, 415, 401 with
and without credentials, keep-alive, label-pattern paths, and
pipe-delimited collections. Header params now look up lowercased field
names; enum allow-lists render unescaped.
Add the deterministic cpp-boost-beast-server petstore sample (builds
clean with -Wall and serves 501 stubs / 400 / 404 over HTTP/1.1), the
generated generator documentation, and a three-OS sample build workflow
covering Boost json+url consumers. API headers now emit the model
namespace using-directive only when an API actually references a
generated model class (map-only APIs such as store inventory compile
standalone).
…erage

Runtime: anonymous security alternatives (security: []) now bypass the
authorization gate; 413 body-limit responses close the connection instead
of re-reading leftover body bytes as a new request; responses mirror the
request HTTP version and keep-alive preference; HttpServer::stop posts
the acceptor close onto its strand; ResponderCore's completion flag is
atomic and the handler-exception 500 routes through the guard; 401s
carry WWW-Authenticate for http-scheme challenges; cookie names strip
leading OWS; BodyJson handles uint64 numbers above INT64_MAX without
throwing past the invalid_argument catch and supports any-type bodies;
parseScalar enforces whole-input match and rejects inf/nan; problem JSON
escapes bytes >= 0x7f to keep bodies valid UTF-8.

Generator: request-body and parameter $refs are resolved before fact
extraction so bodies and constraints survive; declared media-type
parameters are normalized out of kMediaTypes; ranged status codes
(2XX) and oauth2/openIdConnect/mutualTLS schemes are rejected at
generation; integer/number/bool enums are validated at runtime like
string enums; header and cookie params gain enum checks; route shape
keys mirror the runtime splitter; dead x-server-route/kind/inner facts
and the bodyKind helper are removed; shared OperationFacts gains the
Apache header.

Loopback coverage: anonymous-op bypass against a denying authorizer,
integer-enum rejection/acceptance, and conventional spaced Cookie
headers.
…tests

Generation gate:
- reject array/object cookie params, non-scalar array items, object
  params other than query deepObject string maps, heterogeneous enums
- fix malformed security-scheme diagnostic quoting

Runtime:
- splitMatrixExploded(): strip repeated name= per element for
  style=matrix explode=true path parameters
- router literal-over-parameter ranking (most literal segments wins,
  ties keep registration order) so /pets/bulk beats /pets/{petId}
- re-arm the stream timer in send_response so deferred handler
  completions are not aborted by an expired read deadline
- mirror request version/keep-alive on the 413 body_limit path
- deepObject required-missing now reports 400
- shared param-constraints partial: uniform pattern/length/bound
  ladders across query, header, and cookie scalar parameters
- jsonEscape passes UTF-8 through; escapes only C0 controls and DEL

Rendering: minimum/maximum bounds emit long-double-safe literals
(plain integers gain .0), so int64 extremes compile under -Wall.

Tests: four new gate rejection tests plus a deepObject acceptance
test; runtime test hardened (compiler/Boost skip guards, file-based
output redirection, process-tree termination on timeout) and a new
validation-disabled end-to-end case; loopback driver covers matrix
explode, spaceDelimited, deepObject required, literal ranking,
deferred completions, WWW-Authenticate challenges, HTTP/1.0
mirroring incl. 413, cookie decoding, and duplicate-completion
guarding; locator precedence test now probes a template present in
both embedded dirs.

CI: Windows sample job installs boost-asio/beast/multiprecision
ports (the build failed on missing beast and multiprecision headers).

Docs: README ServerOptions table for readTimeoutSeconds/bodyLimitBytes;
sample regenerated deterministically.
@bold84
bold84 force-pushed the pr/cpp-boost-beast-server branch from f8d7a8b to 99775fa Compare August 26, 2026 18:45
bold84 added 11 commits August 27, 2026 02:23
…rity schemes

AllGeneratorsTest requires every registered generator to generate from
the canonical 3_0/petstore.yaml, which declares XML/form/multipart
payloads and an oauth2 scheme; the previous hard-reject gate broke that
contract for the entire project on every CI leg.

- preprocessOpenAPI now logs precise warnings for non-JSON request
  media types, non-JSON response media types, and security scheme types
  with no runtime credential extractor, while still throwing for the
  compile-breaking/route-ambiguous categories (parameter styles and
  shapes, cookie containers, non-scalar array items, non-deepObject
  object params, heterogeneous enums, ambiguous routes, ranged codes)
- the assembler filters declared request media types down to JSON:
  mixed bodies accept JSON and answer 415 to the rest, JSON-less bodies
  drop the typed body field entirely (hasBody=false, compiles clean)
- unsupported scheme types remain in the route table as declared; the
  runtime's structurallySatisfied() denies those requests with 401
  instead of the generation failing
- five rejection tests rewritten as degrade tests asserting the exact
  generated contracts, plus a canonical-petstore regression test
- verified: AllGeneratorsTest 855/855, beast suites 183 green, both
  samples regenerate with zero diff
The feature set advertised FormUnencoded/FormMultipart support while
the server never parses those payloads (they degrade to no typed body).
Exclude both parameter features and regenerate the generator docs page.
… variant bodies

Move the 16 chunked OAS 3.1 schema-IR templates from the client-only
embedded dir into cpp-boost-beast-common so the server generator (whose
own dir never carried them) can reach the chunked IR path for large
specs, and pin resolution from both generators with a locator test.

Wipe the inherited DefaultCodegen typeMapping before seeding it, same
as the client generator: AnyType -> oas_any_type_not_mapped is a
placeholder header this family never provides, and the OpenAI corpus
(FunctionToolParam output_schema anyOf) reaches it through a freeform
branch that must resolve to boost::json::value.

Render std::variant, std::optional, and std::monostate bodies in
BodyJson.h: composition-typed responses serialize the active branch via
std::visit, and null branches serialize as JSON null instead of failing
to compile.
…d-body JSON members

Parameter shapes the JSON runtime cannot decode (content-style, form
fields, object/array queries outside deepObject/scalar rules, cookie
containers, heterogeneous enums, enum-class dataTypes) now degrade to a
dropped handler field with a warning instead of rejecting the document,
mirroring the media-type policy so real-world corpora generate code.
The OpenAI spec drove the rules: the classifier keys off the resolved
dataType the templates emit, so parseScalar can never see a type without
an overload.

Request-body recovery: a multipart+JSON body whose JSON member $refs a
generated model now types the handler body exactly (the model's include
is appended via toModelImport when DefaultCodegen did not import it);
models aliased to std::variant degrade to no typed body because
fromJsonLeaf cannot decode unions; a body model colliding with the
generated request struct is namespace-qualified. The model
using-directive flag accounts for recovered bodies. The gate now
rejects only non-deterministic routing (ambiguous templates, ranged
codes). Tests: six degrade rewrites plus four new contracts.
…e the right logger

BodyJson.h declares a std::optional overload but relied on <variant>
transitively providing <optional>; include it directly. The shared model
codegen hardcoded its logger to CppBoostBeastClientCodegen, so server
degrade warnings were attributed to the client class; use getClass().
Regenerated petstore server sample picks up the new include.
… rule

ArchUnitRulesTest requires slf4j Logger fields to be non-public,
non-static and final (PR OpenAPITools#8799); the new assembler logger was static.
Both call sites are instance methods, so the field becomes an instance
logger.
…tests

- Router: tokenize embedded path expressions in one segment; shape keys
  keep literal text distinct from placeholders
- RequestContext held via shared_ptr through the handler/service chain
- Optional request bodies no longer fail presence checks when absent
- Multi-tag operations get per-operation contract type names
- ParamCodecs: float narrowing range checks, exclusive numeric bounds,
  collection/item constraints (new param-container-constraints include),
  strict label/matrix percent-encoded codecs, exact integer comparisons
- Problem JSON: sanitize malformed UTF-8 in error details
- Gate: wildcard response media no longer accepted as JSON; request-body
  schema selected per declared media; form-encoding hard-reject moved to
  a server-side override so the shared model path no longer rejects the
  client corpus; MultiServer feature claim set to false
- Generated CMake: warnings are errors by default (opt-out flag added)
- README quick-start renders real operations and attach sequence
- Runtime regression spec/driver extended to cover all of the above
… regression

The sample workflow ran the runtime test with -pl modules/openapi-generator
alone, so the 7.26.0-SNAPSHOT sibling (openapi-generator-core) could not be
resolved from the snapshot repo on a clean runner checkout. Build the
upstream modules with -am and tolerate modules without the selected test.
…l-closed codecs, review gaps

- BodyJson: unwrap tagged CompositionBranchValue branches so oneOf
  responses whose C++ types collide serialize through std::visit
- ParamCodecs: reject hex floats explicitly, accept ERANGE underflow,
  drop the dead errno store; document the whole-input match
- param-constraints / param-container-constraints: guard std::regex
  construction so an out-of-subset pattern fails closed with 400
  instead of retry-throwing 500 per request (absent/empty skip it)
- Assembler: apiNamespace is constructor-injected (the operations-map
  merge runs after postProcessOperationsWithModels, which made the
  collision guard inert); mixed bodies type the JSON member model even
  when an unparseable member came first; README facts carry
  model-namespace-qualified send types
- Codegen: exclude Host/BasePath global features (never mounted); the
  server overrides the shared form-encoding reject because it degrades
  flattened form fields with a warning instead of aborting
- Workflow: trigger the sample CI on generator, template, and test
  resource changes; add generation/runtime coverage for the new paths
Deterministic re-run of bin/generate-samples.sh and
bin/utils/export_generator.sh after the fix commit: README quick-start
qualifies model response types, BodyJson.h gains the tagged-branch
serializer, ParamCodecs.h gains the hex-float gate, and the feature
table now reports Host/BasePath as unsupported.
@bold84
bold84 marked this pull request as ready for review August 27, 2026 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant