Skip to content

build: bump cpp-httplib to 0.47.0 - #653

Merged
andiwand merged 2 commits into
mainfrom
build/bump-cpp-httplib
Aug 2, 2026
Merged

build: bump cpp-httplib to 0.47.0#653
andiwand merged 2 commits into
mainfrom
build/bump-cpp-httplib

Conversation

@andiwand

@andiwand andiwand commented Aug 2, 2026

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

cpp-httplib/0.16.3 has been dropped from conan-center-index — only 0.28.0 and 0.47.0 are in recipes/cpp-httplib/config.yml now — so the pin had stopped receiving fixes altogether.

Why this needed conan-io/conan-center-index#30607 first

Every version from 0.28.0 on compiles the CFHost-based asynchronous resolver behind CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO. 0.16.3 does not contain that code path at all, which is the only reason the current pin links on Apple.

It matters here even though odr uses the server only, because the server reaches the resolver too:

Server::create_server_socket -> detail::create_socket -> getaddrinfo_with_timeout

The old recipe linked CoreFoundation/CFNetwork only in the narrow macOS use_macos_keychain_certs case, and never for iOS — so the xcframework's device slice would have failed to link with undefined _CFHostCreateWithName / _CFRunLoopGetCurrent. The fixed recipe links them on every Apple OS whenever the resolver is compiled in. Verified on the freshly built device slice:

$ otool -L apple/build/apple-ios-armv8/.../OdrCoreObjC | grep -iE 'CFNetwork|CoreFoundation'
    /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
    /System/Library/Frameworks/CFNetwork.framework/CFNetwork

Measured effect: the Apple suite's teardown

HttpServerTests/testServesARenderedView (XCTest, URLSession), instrumented to time the request and the teardown separately, two runs each:

cpp-httplib fetch stop() total
0.16.3 0.019 / 0.011 s 5.013 / 5.010 s ~5.03 s
0.47.0 0.016 / 0.012 s 0.011 / 0.010 s ~0.03 s

Serving was never slow — the ~5 s sat entirely in teardown, where HttpServer::stop() waits for listen() to return and listen() waits for its workers.

Scope of that claim, deliberately narrow. This is the Apple/URLSession path only; it is not a general "keep-alive no longer delays teardown" result, and I checked before claiming it. On 0.47.0 the JNI suite still needs its Connection: close: dropping it takes serveFile() from 0.030 s to 5.07 s (three runs each). So a pooled keep-alive connection can still cost the keep-alive timeout on 0.47.0, and jni/tests/.../HttpServerTest.java keeps its workaround. What changed for the Apple path specifically I have not pinned down, so I am not asserting a mechanism.

The server-side read/write and keep-alive constants are 5 in both versions, so whatever changed is upstream behaviour, not a tunable we could have set.

Not to be confused with the 1 ms poll loop in stop() that waits for a listen() between our mutex and httplib's is_running_ — unrelated, bounded, and still there.

On use_non_blocking_getaddrinfo

Left at its default (True). It is the option that gives the bind-time DNS resolution a timeout at all — turning it off swaps two always-present Apple system frameworks for an unbounded blocking getaddrinfo, which is the wrong direction for a server that may bind to a hostname.

To be explicit about one thing: this does not remove the poll loop in HttpServer::Impl::stop(). That loop waits for a listen() that is between our mutex and httplib's is_running_ = true, and 0.28.0's new Server::decommission() does not close that window — listen_internal() checks is_decommissioned and sets is_running_ as two separate unsynchronised steps, so a stop() that skipped Server::stop() on a momentarily-false is_running() would leave the accept loop up. The loop stays, and is untouched here.

Verification

  • Apple slices apple-macos-armv8, apple-macos-x86_64, apple-ios-armv8 all build and link
  • HttpServerTests (testServesARenderedView, testStopIsIdempotent) pass against 0.47.0
  • Our httplib surface — bind_to_port, bind_to_any_port, set_socket_options, set_exception_handler, listen_after_bind, is_running, stop — is unchanged in 0.47.0; the one source change needed was the bind-retry reset below

conan.lock is edited on the single line rather than regenerated: a full scripts/conan_lock run on this machine also re-pins pugixml and drops the option-gated entries, which does not belong in a version bump.

Bind retry (addressing the review comment)

cpp-httplib 0.47.0 sets is_decommissioned on the server when a bind fails, and every later bind_internal() short-circuits on it, so "try a preferred port, fall back to port 0" threw ServerBindFailed forever. 0.16.3 has no such flag — a regression the bump introduces. The failing path now calls Server::stop(), which is what clears it and, with nothing listening, does nothing else. Recreating the server object would also work but would drop the exception handler, socket options and routes the constructor installs.

HttpServer.bind_can_be_retried_after_it_failed pins it — it fails with server bind failed: 127.0.0.1:0 without the reset.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 65fec6f8f3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread conanfile.py
andiwand and others added 2 commits August 2, 2026 14:55
0.16.3 was dropped from conan-center-index - only 0.28.0 and 0.47.0 are left -
so the pin had stopped receiving fixes entirely.

The bump was blocked until conan-io/conan-center-index#30607 landed. Every
version from 0.28.0 on compiles the CFHost-based asynchronous resolver behind
`CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO`, which 0.16.3 does not have at all,
and the recipe linked CoreFoundation/CFNetwork only in the narrow macOS
keychain-certs case - never for iOS. Since `Server::create_server_socket` goes
through `detail::create_socket` -> `getaddrinfo_with_timeout`, the server-only
build pulls that resolver in, so the xcframework would have failed to link on
the device slice. The fixed recipe links the frameworks on every Apple OS
whenever the resolver is compiled in, and the iOS binary now carries both.

Measured on the http server suite, three runs each: serving a rendered view
takes 5.03 s on 0.16.3 and 0.03 s on 0.47.0. The server-side read and keep-alive
timeout constants are identical between the two, so this is an upstream
behaviour fix rather than a tunable.

`use_non_blocking_getaddrinfo` stays at its default of True: it is what gives
the bind-time resolution a timeout at all, and turning it off would trade two
Apple frameworks for an unbounded blocking `getaddrinfo`.

The lockfile is edited on the one line rather than regenerated - a full
`scripts/conan_lock` run also re-pins pugixml and drops the option-gated
entries, which does not belong in a version bump.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YSePnR4Jcj5cq3H1D11xcd
cpp-httplib 0.47.0 decommissions the server when a bind fails - both
`bind_to_port` and `bind_to_any_port` set `is_decommissioned`, and every later
`bind_internal()` short-circuits on it - so the "try a preferred port, fall back
to any port" pattern stopped working: the second `HttpServer::bind` threw
`ServerBindFailed` forever. 0.16.3 has no such flag and allowed the retry, which
makes this a regression the bump introduces rather than anything new in the
wrapper.

`Server::stop()` is what clears the flag, and with nothing listening it does
nothing else, so the failing bind path calls it before throwing. Recreating the
server object would have worked too but would drop the exception handler, the
socket options and the mounted routes the constructor installs.

`bind_can_be_retried_after_it_failed` fails with
`server bind failed: 127.0.0.1:0` without the reset.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YSePnR4Jcj5cq3H1D11xcd
@andiwand
andiwand force-pushed the build/bump-cpp-httplib branch from 115a030 to 9977c60 Compare August 2, 2026 12:56
@andiwand
andiwand enabled auto-merge (squash) August 2, 2026 12:56
@andiwand
andiwand merged commit 64f2b58 into main Aug 2, 2026
34 checks passed
@andiwand
andiwand deleted the build/bump-cpp-httplib branch August 2, 2026 13:06
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