Skip to content

Commit 1da2ee1

Browse files
committed
Merge TASK-042: Write RELEASE_NOTES.md for v2.0 (PRD §2 documentation NFR, §13 documentation deliverable)
2 parents c91555d + 090203c commit 1da2ee1

6 files changed

Lines changed: 660 additions & 8 deletions

File tree

Makefile.am

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ endif
3939
endif
4040

4141
EXTRA_DIST = libhttpserver.pc.in $(DX_CONFIG) scripts/extract-release-notes.sh scripts/validate-version.sh \
42-
scripts/check-examples.sh scripts/check-readme.sh scripts/verify-installed-examples.sh \
42+
scripts/check-examples.sh scripts/check-readme.sh scripts/check-release-notes.sh \
43+
scripts/verify-installed-examples.sh \
4344
test/headers/consumer_direct.cpp test/headers/consumer_detail.cpp test/headers/consumer_umbrella.cpp \
4445
test/headers/consumer_post_umbrella.cpp \
4546
test/headers/consumer_umbrella_no_backend.cpp
@@ -282,7 +283,7 @@ check-hygiene:
282283
# shared staged install to avoid paying two full `make install` costs on
283284
# every `make check`. Both sub-checks can still be invoked standalone (they
284285
# will do their own install when CHECK_*_SHARED is not set).
285-
check-local: check-headers check-examples check-readme
286+
check-local: check-headers check-examples check-readme check-release-notes
286287
@echo "=== Shared staged install for check-install-layout and check-hygiene ==="
287288
@rm -rf $(abs_top_builddir)/.shared-check-stage
288289
@$(MAKE) $(AM_MAKEFLAGS) install DESTDIR=$(abs_top_builddir)/.shared-check-stage >check-shared-install.log 2>&1 || { \
@@ -311,7 +312,14 @@ check-readme:
311312
@echo "=== check-readme: enforce TASK-041 invariants on README.md ==="
312313
@$(top_srcdir)/scripts/check-readme.sh
313314

314-
.PHONY: check-headers check-install-layout check-hygiene check-examples check-readme
315+
# TASK-042: enforce static invariants on RELEASE_NOTES.md
316+
# (required v1/v2 tokens, sections, same-line rename pairs, arch citations,
317+
# disclaimer). Run as part of `make check` via check-local.
318+
check-release-notes:
319+
@echo "=== check-release-notes: enforce TASK-042 invariants on RELEASE_NOTES.md ==="
320+
@$(top_srcdir)/scripts/check-release-notes.sh
321+
322+
.PHONY: check-headers check-install-layout check-hygiene check-examples check-readme check-release-notes
315323

316324
# TASK-039: top-level convenience rule that descends into test/ to
317325
# build and run the bench binaries (see test/Makefile.am and

RELEASE_NOTES.md

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
# libhttpserver v2.0 — Release Notes
2+
3+
> **Status:** Informational, not a compatibility commitment.
4+
> This document summarises every public-surface change in v2.0 to help v1
5+
> consumers port. It is not part of the API contract and is not exhaustive
6+
> — when in doubt, the headers in `src/httpserver/` are authoritative.
7+
8+
## TL;DR
9+
10+
libhttpserver v2.0 is a **clean cutover** from the v1.x line: no shims, no
11+
deprecated overloads, no inline namespace. Handlers are now lambdas
12+
(`ws.on_get("/", [](auto&){ return http_response::string("hi"); })`);
13+
responses are value types built from `http_response::string` / `file` /
14+
`iovec` / `pipe` / `empty` / `deferred` / `unauthorized` factories;
15+
request getters return `const&` / `string_view` and never insert on miss;
16+
the threading and error-propagation contracts are documented (DR-008,
17+
DR-009); features disabled at build time report at runtime via
18+
`feature_unavailable` or a documented sentinel value instead of vanishing
19+
from the API. SOVERSION bumps from 1 to 2 and v2 ships as a
20+
`libhttpserver2` binary that is parallel-installable with `libhttpserver1`.
21+
v1.x is end-of-life on the day v2.0 ships.
22+
23+
## What's gone
24+
25+
- **Response subclasses.** `string_response`, `file_response`,
26+
`iovec_response`, `pipe_response`, `deferred_response`, `empty_response`,
27+
`basic_auth_fail_response`, `digest_auth_fail_response` are removed.
28+
Build responses through the `http_response::*` factory chain instead
29+
(see "What's renamed" for the 1:1 mapping).
30+
- **Raw-pointer registration.** `register_resource(string, http_resource*, bool family)`
31+
is gone. Use `register_path(unique_ptr|shared_ptr)` or
32+
`register_prefix(unique_ptr|shared_ptr)`.
33+
`register_ws_resource(string, websocket_handler*)` is gone. Use the
34+
smart-pointer overload.
35+
- **`sweet_kill`.** Removed. Use `stop_and_wait()` (or `stop()` for the
36+
signal-only form).
37+
- **IP allow/deny verbs.** `ban_ip`, `unban_ip`, `allow_ip`,
38+
`disallow_ip` are removed. Use `block_ip` / `unblock_ip`.
39+
- **Paired `no_*` boolean setters.** `no_basic_auth`, `no_digest_auth`,
40+
`no_ssl`, `no_debug`, `no_pedantic`, `no_deferred`, `no_regex_checking`,
41+
`no_ban_system`, `no_post_process`, `no_single_resource`, `no_ipv6`,
42+
`no_dual_stack` are removed. The remaining single setter takes a `bool`:
43+
e.g. `cw.ssl(false)` replaces `cw.no_ssl()`.
44+
- **`#define` constants in public headers.** `DEFAULT_WS_PORT`,
45+
`DEFAULT_WS_TIMEOUT`, and the `*_ERROR` / `*_RESPONSE` macros are gone.
46+
Their `constexpr` replacements live in the `httpserver::constants`
47+
namespace.
48+
- **`gnutls_session_t`-returning methods on `http_request`.** Removed.
49+
Use the high-level accessors: `get_client_cert_dn()`,
50+
`get_client_cert_issuer_dn()`, `get_client_cert_fingerprint()`,
51+
`get_cipher_suite()`, `get_protocol_version()`, `get_client_kx()`,
52+
`get_client_cert_type()`, `get_client_credential_type()`.
53+
- **Public virtuals on `http_response`.** `get_raw_response`,
54+
`decorate_response`, `enqueue_response` are gone. `http_response` is no
55+
longer an open base class; it is a value type with no virtual methods.
56+
- **`#ifdef HAVE_BAUTH` / `HAVE_DAUTH` / `HAVE_GNUTLS` / `HAVE_WEBSOCKET`
57+
guards in public headers.** Removed. The public API is now
58+
build-flag-invariant: every method exists on every build. Calls into a
59+
feature disabled at configure time throw `feature_unavailable` or
60+
return a documented sentinel; query the runtime configuration via
61+
`ws.features()`.
62+
- **Public transitive includes of backend C headers.** `<microhttpd.h>`,
63+
`<pthread.h>`, `<gnutls/gnutls.h>`, `<sys/socket.h>` no longer leak
64+
through `<httpserver.hpp>`. A consumer that needs MHD or GnuTLS types
65+
must include them directly.
66+
- **The implicit conversion** `webserver ws = cw;` (where `cw` is a
67+
`create_webserver`). The constructor is now `explicit`.
68+
69+
## What's new
70+
71+
- **Lambda registration.** `ws.on_get(path, handler)`, `on_post`,
72+
`on_put`, `on_delete`, `on_patch`, `on_options`, `on_head`. The handler
73+
is `http_response(const http_request&)`. No subclass required.
74+
- **Runtime-method registration.** `ws.route(http_method | method_set,
75+
path, handler)` registers a single handler for a method known only at
76+
runtime, or atomically for a bitmask of methods.
77+
- **Smart-pointer resource registration.** `register_path(path, h)` and
78+
`register_prefix(path, h)` accept `std::unique_ptr<http_resource>` or
79+
`std::shared_ptr<http_resource>`. The boolean `family` flag that used
80+
to live as the third positional argument of `register_resource` is gone
81+
— the choice is encoded in the method name.
82+
- **`http_response` factory chain.** `http_response::string(body)`,
83+
`http_response::file(path)`, `http_response::iovec(entries)`,
84+
`http_response::pipe(fd)`, `http_response::empty(status)`,
85+
`http_response::deferred(...)`,
86+
`http_response::unauthorized(scheme, realm, ...)`. Each returns a
87+
value-type `http_response`. Chain fluent mutators:
88+
`.with_status(int)`, `.with_header(name, value)`, `.with_footer(...)`,
89+
`.with_cookie(...)`.
90+
- **`feature_unavailable`.** Public `std::runtime_error` subclass thrown
91+
by API entry points whose backend feature was disabled at configure
92+
time (e.g. calling `ssl_cert(...)` on a `HAVE_GNUTLS=no` build).
93+
- **`webserver::features()`.** Returns a struct of `bool`s describing
94+
which optional features are linked in (TLS, basic auth, digest auth,
95+
websocket, …). Use this for runtime branching instead of `#ifdef HAVE_*`.
96+
- **`iovec_entry`.** Public POD struct (`{void* iov_base; size_t iov_len;}`)
97+
used by `http_response::iovec`. Replaces the opaque scatter-gather
98+
type that v1 leaked from `<sys/uio.h>`.
99+
- **`http_method` / `method_set`.** Strongly-typed enum and bitmask for
100+
HTTP methods. `route()` accepts either; `http_resource::allow_methods`
101+
uses `method_set`.
102+
- **`httpserver::constants` namespace.** `constexpr` replacements for
103+
every removed `#define`.
104+
105+
## What's renamed (v1 → v2)
106+
107+
Every entry below is a single line so a v1 user can grep for the v1 name
108+
and see the v2 replacement.
109+
110+
| v1 | v2 |
111+
|---|---|
112+
| `sweet_kill` | `stop_and_wait` |
113+
| `ban_ip` | `block_ip` |
114+
| `unban_ip` | `unblock_ip` |
115+
| `allow_ip` | `unblock_ip` (or `block_ip` to deny) |
116+
| `disallow_ip` | `block_ip` (or `unblock_ip` to allow) |
117+
| `not_found_resource` (setter) | `not_found_handler` |
118+
| `method_not_allowed_resource` (setter) | `method_not_allowed_handler` |
119+
| `internal_error_resource` (setter) | `internal_error_handler` |
120+
| `render_GET` | `render_get` |
121+
| `render_POST` | `render_post` |
122+
| `render_PUT` | `render_put` |
123+
| `render_DELETE` | `render_delete` |
124+
| `render_HEAD` | `render_head` |
125+
| `render_OPTIONS` | `render_options` |
126+
| `render_PATCH` | `render_patch` |
127+
| `render_CONNECT` | `render_connect` |
128+
| `render_TRACE` | `render_trace` |
129+
| `webserver(create_webserver const&)` — implicit | `explicit webserver(create_webserver const&)` |
130+
| `register_resource(string, http_resource*, bool family)` — raw pointer + bool flag | `register_path` / `register_prefix` taking `unique_ptr` or `shared_ptr` |
131+
| `register_ws_resource(string, websocket_handler*)` — raw pointer | `register_ws_resource(string, unique_ptr<websocket_handler>)` (and a `shared_ptr` overload) |
132+
| `string_response` | `http_response::string` |
133+
| `file_response` | `http_response::file` |
134+
| `iovec_response` | `http_response::iovec` |
135+
| `pipe_response` | `http_response::pipe` |
136+
| `empty_response` | `http_response::empty` |
137+
| `deferred_response` | `http_response::deferred` |
138+
| `basic_auth_fail_response` | `http_response::unauthorized` |
139+
| `digest_auth_fail_response` | `http_response::unauthorized` |
140+
141+
## What changed semantically
142+
143+
- **Handlers return `http_response` by value.** v1 returned
144+
`std::unique_ptr<http_response>` (and earlier `std::shared_ptr<http_response>`);
145+
v2 returns a value. The framework moves it into the dispatch path. No
146+
heap allocation is required for small responses (small-buffer optimisation
147+
inside `http_response`).
148+
- **`http_request` getters return `const&` / `string_view`.** v1's
149+
`get_header(name)` (and `get_arg`, `get_cookie`, `get_footer`) returned
150+
by value and inserted an empty entry into the request map on miss; v2's
151+
versions return a reference / `string_view` and never mutate the
152+
request. Container getters (`get_headers()`, `get_args()`, …) return
153+
`const&` to the underlying map.
154+
- **`http_response::get_header` / `get_footer` / `get_cookie` are
155+
const and do not insert on miss.** Mutation uses the
156+
`with_header` / `with_footer` / `with_cookie` chain, which returns
157+
`*this` by reference (`&` on lvalue calls, `&&` on rvalue calls) for
158+
fluent assembly.
159+
- **`webserver(create_webserver const&)` is `explicit`.** Direct-init
160+
`webserver ws{cw};` rather than copy-init `webserver ws = cw;`.
161+
162+
## Threading
163+
164+
The threading contract is now documented; in v1 it was implicit.
165+
See [README.md "Threading contract"](README.md), architecture §5.1
166+
([specs/architecture/05-cross-cutting.md](specs/architecture/05-cross-cutting.md))
167+
and DR-008 ([specs/architecture/11-decisions/DR-008.md](specs/architecture/11-decisions/DR-008.md))
168+
for the full statement. The load-bearing points for porters:
169+
170+
- Handler invocations from MHD worker threads run concurrently. Shared
171+
state owned by an `http_resource` subclass (or captured by a lambda)
172+
must be synchronised by the application.
173+
- `start()` is one-shot; **do not** call `stop()` or `stop_and_wait()`
174+
from inside a handler thread — MHD's joinable internal thread cannot
175+
join itself and the call aborts the process (DR-008).
176+
- `~webserver()` runs `stop_and_wait()` if the server is still running,
177+
so the same self-join restriction applies to destruction from a
178+
handler thread.
179+
180+
## Error propagation
181+
182+
The error-propagation contract is now documented; in v1 it was implicit.
183+
See [README.md "Error propagation"](README.md), architecture §5.2
184+
([specs/architecture/05-cross-cutting.md](specs/architecture/05-cross-cutting.md))
185+
and DR-009 ([specs/architecture/11-decisions/DR-009.md](specs/architecture/11-decisions/DR-009.md))
186+
for the full statement. The load-bearing points for porters:
187+
188+
- A handler that throws lands at the configured
189+
`internal_error_handler` (a `std::function<http_response(const http_request&, std::string_view)>`).
190+
The `string_view` carries `what()` from the caught exception. Default
191+
behaviour: log and return `500`.
192+
- Calling an API entry point whose backend feature is disabled at
193+
configure time (e.g. `ssl_cert(...)` on a `HAVE_GNUTLS=no` build)
194+
throws `feature_unavailable` — a public `std::runtime_error` subclass.
195+
Build-flag-disabled features therefore surface at **runtime** rather
196+
than disappearing from the API at compile time; query
197+
`ws.features()` to branch defensively.
198+
- The framework never swallows exceptions silently. Anything that
199+
escapes a handler is logged via the configured `log_error` callback
200+
before the `internal_error_handler` fires.
201+
202+
## Build prerequisites
203+
204+
- **C++20 floor.** libhttpserver 1.x's last release was C++17-compatible;
205+
v2.0 drops C++17. Concepts, `<bit>`, `<span>`, and `consteval` appear
206+
in public headers.
207+
- **Toolchains known good out of the box:** Debian 13 GCC 14.2,
208+
RHEL 10 GCC 14, FreeBSD 14 Clang 18+, current Apple Clang, and
209+
Homebrew GCC 15+.
210+
- **RHEL 9.** Stock GCC 11 on RHEL 9 is too old. Install Red Hat's
211+
`gcc-toolset-14` overlay and `source scl_source enable gcc-toolset-14`
212+
before configuring.
213+
214+
See [specs/architecture/08-build-and-packaging.md](specs/architecture/08-build-and-packaging.md)
215+
for the full matrix.
216+
217+
## SOVERSION and packaging
218+
219+
SOVERSION bumps from `1` to `2`. v2 ships as `libhttpserver2` (binary)
220+
with `libhttpserver2-dev` / `libhttpserver2-devel` for headers and
221+
pkg-config. v1's `libhttpserver1` and v2's `libhttpserver2` are
222+
**parallel-installable**: applications mid-port can link against both
223+
side by side, including under the same `/usr` prefix.
224+
225+
There is **no inline namespace** and **no symbol-versioning script**:
226+
v2 is a clean rename, not an ABI overlay over v1. v1.x is end-of-life
227+
on the day v2.0 ships — there is no v1 maintenance branch (PRD §1,
228+
DR-011 ([specs/architecture/11-decisions/DR-011.md](specs/architecture/11-decisions/DR-011.md))).
229+
230+
## See also
231+
232+
- [README.md](README.md) — full v2.0 introduction and worked examples.
233+
- [examples/](examples/) — every example is v2.0-idiomatic.
234+
- [specs/product_specs.md](specs/product_specs.md) §3.1–§3.7 — the
235+
authoritative requirement set behind each change.
236+
- [ChangeLog](ChangeLog) — formal per-version log.

0 commit comments

Comments
 (0)