httpp 0.6.0__tar.gz → 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. {httpp-0.6.0 → httpp-0.7.0}/.github/workflows/cmake.yml +1 -1
  2. {httpp-0.6.0 → httpp-0.7.0}/.github/workflows/orchestrator.yml +12 -1
  3. httpp-0.7.0/.github/workflows/wheels.yml +103 -0
  4. {httpp-0.6.0 → httpp-0.7.0}/.gitmodules +3 -0
  5. httpp-0.7.0/CHANGELOG.md +198 -0
  6. {httpp-0.6.0 → httpp-0.7.0}/CMakeLists.txt +94 -1
  7. httpp-0.7.0/PKG-INFO +430 -0
  8. httpp-0.7.0/README.md +404 -0
  9. httpp-0.7.0/benchmarks/asgi_app.py +21 -0
  10. httpp-0.7.0/benchmarks/bench.py +99 -0
  11. httpp-0.7.0/benchmarks/bench_validate.py +67 -0
  12. httpp-0.7.0/benchmarks/httpp_app.py +50 -0
  13. httpp-0.7.0/cmake/EmbedFile.cmake +114 -0
  14. httpp-0.7.0/cmake/httpp_mbedtls_config.h +24 -0
  15. httpp-0.7.0/cmake/utils/embedded_files.in.hpp +12 -0
  16. httpp-0.7.0/docs/Architecture.md +404 -0
  17. httpp-0.7.0/docs/agent.md +381 -0
  18. httpp-0.7.0/docs/checklist.md +290 -0
  19. httpp-0.7.0/examples/python/01_hello_server.py +16 -0
  20. httpp-0.7.0/examples/python/02_path_params.py +23 -0
  21. httpp-0.7.0/examples/python/03_request_object.py +27 -0
  22. httpp-0.7.0/examples/python/04_responses.py +44 -0
  23. httpp-0.7.0/examples/python/05_http_methods.py +48 -0
  24. httpp-0.7.0/examples/python/06_async_handlers.py +37 -0
  25. httpp-0.7.0/examples/python/07_serve_directory.py +23 -0
  26. httpp-0.7.0/examples/python/08_error_handling.py +30 -0
  27. httpp-0.7.0/examples/python/09_client.py +37 -0
  28. httpp-0.7.0/examples/python/10_request_builder.py +44 -0
  29. httpp-0.7.0/examples/python/11_download.py +35 -0
  30. httpp-0.7.0/examples/python/12_hooks_and_settings.py +81 -0
  31. httpp-0.7.0/examples/python/13_streaming_and_files.py +84 -0
  32. httpp-0.7.0/examples/python/14_async_client.py +65 -0
  33. httpp-0.7.0/examples/python/15_progress_and_url.py +31 -0
  34. httpp-0.7.0/handoff.md +479 -0
  35. httpp-0.7.0/include/httpp/client.hpp +299 -0
  36. httpp-0.7.0/include/httpp/download.hpp +129 -0
  37. httpp-0.7.0/include/httpp/log.hpp +31 -0
  38. {httpp-0.6.0 → httpp-0.7.0}/include/httpp/progress.hpp +2 -1
  39. httpp-0.7.0/include/httpp/server.hpp +192 -0
  40. httpp-0.7.0/include/httpp/sha256.hpp +24 -0
  41. httpp-0.7.0/include/httpp/swagger_ui.hpp +30 -0
  42. {httpp-0.6.0 → httpp-0.7.0}/include/httpp/url.hpp +8 -0
  43. {httpp-0.6.0 → httpp-0.7.0}/include/httpp.h +2 -1
  44. {httpp-0.6.0 → httpp-0.7.0}/pyproject.toml +20 -4
  45. {httpp-0.6.0 → httpp-0.7.0}/src/bindings/python/CMakeLists.txt +13 -2
  46. httpp-0.7.0/src/bindings/python/_validate_cy.pyx +898 -0
  47. {httpp-0.6.0 → httpp-0.7.0}/src/bindings/python/httpp/__init__.py +13 -2
  48. {httpp-0.6.0 → httpp-0.7.0}/src/bindings/python/httpp/__main__.py +34 -0
  49. httpp-0.7.0/src/bindings/python/httpp/_http.py +130 -0
  50. httpp-0.7.0/src/bindings/python/httpp/_params.py +448 -0
  51. httpp-0.7.0/src/bindings/python/httpp/_run.py +167 -0
  52. httpp-0.7.0/src/bindings/python/httpp/_schema.py +409 -0
  53. httpp-0.7.0/src/bindings/python/httpp/_validate.py +226 -0
  54. httpp-0.7.0/src/bindings/python/httpp/errors.py +50 -0
  55. httpp-0.7.0/src/bindings/python/httpp/models.py +497 -0
  56. httpp-0.7.0/src/bindings/python/httpp/progress.py +34 -0
  57. httpp-0.7.0/src/bindings/python/httpp/server.py +900 -0
  58. httpp-0.7.0/src/bindings/python/httpp_cy.pyx +1562 -0
  59. httpp-0.7.0/src/core/async_http.cpp +654 -0
  60. httpp-0.7.0/src/core/async_server.cpp +654 -0
  61. httpp-0.7.0/src/core/client.cpp +799 -0
  62. httpp-0.7.0/src/core/download.cpp +608 -0
  63. httpp-0.7.0/src/core/internal/async_http.hpp +71 -0
  64. httpp-0.7.0/src/core/internal/async_server.hpp +95 -0
  65. httpp-0.7.0/src/core/internal/blocking_pool.hpp +73 -0
  66. httpp-0.7.0/src/core/internal/log.hpp +45 -0
  67. httpp-0.7.0/src/core/internal/tls_session.hpp +48 -0
  68. httpp-0.7.0/src/core/internal/worker_pool.hpp +107 -0
  69. httpp-0.7.0/src/core/log.cpp +107 -0
  70. httpp-0.7.0/src/core/mbedtls_alt/threading_alt.h +22 -0
  71. httpp-0.7.0/src/core/mbedtls_threading.cpp +49 -0
  72. {httpp-0.6.0 → httpp-0.7.0}/src/core/progress.cpp +6 -1
  73. httpp-0.7.0/src/core/server.cpp +853 -0
  74. httpp-0.7.0/src/core/sha256.cpp +110 -0
  75. httpp-0.7.0/src/core/swagger_ui.cpp +97 -0
  76. httpp-0.7.0/src/core/tls_session.cpp +210 -0
  77. httpp-0.7.0/src/core/url.cpp +94 -0
  78. httpp-0.7.0/src/llhttp/LICENSE +22 -0
  79. httpp-0.7.0/src/llhttp/VENDORED.md +16 -0
  80. httpp-0.7.0/src/llhttp/include/llhttp.h +925 -0
  81. httpp-0.7.0/src/llhttp/src/api.c +517 -0
  82. httpp-0.7.0/src/llhttp/src/http.c +170 -0
  83. httpp-0.7.0/src/llhttp/src/llhttp.c +10279 -0
  84. httpp-0.7.0/src/third_party/README.md +68 -0
  85. httpp-0.7.0/src/third_party/aixlog/LICENSE +21 -0
  86. httpp-0.7.0/src/third_party/aixlog/aixlog.hpp +1254 -0
  87. {httpp-0.6.0 → httpp-0.7.0}/src/third_party/cpp-httplib/httplib.h +0 -18
  88. httpp-0.7.0/src/third_party/swagger-ui/LICENSE +202 -0
  89. httpp-0.7.0/src/third_party/swagger-ui/NOTICE +2 -0
  90. httpp-0.7.0/src/third_party/swagger-ui/VENDORED.md +10 -0
  91. httpp-0.7.0/src/third_party/swagger-ui/swagger-api-icon.svg +1 -0
  92. httpp-0.7.0/src/third_party/swagger-ui/swagger-ui-bundle.js +2 -0
  93. httpp-0.7.0/src/third_party/swagger-ui/swagger-ui-bundle.js.LICENSE.txt +131 -0
  94. httpp-0.7.0/src/third_party/swagger-ui/swagger-ui.css +3 -0
  95. httpp-0.7.0/tests/cpp/CMakeLists.txt +55 -0
  96. httpp-0.7.0/tests/cpp/local_server.hpp +30 -0
  97. httpp-0.7.0/tests/cpp/test_async_http.cpp +317 -0
  98. httpp-0.7.0/tests/cpp/test_async_https.cpp +262 -0
  99. httpp-0.7.0/tests/cpp/test_async_pool_proxy.cpp +425 -0
  100. httpp-0.7.0/tests/cpp/test_async_server.cpp +518 -0
  101. httpp-0.7.0/tests/cpp/test_certs.hpp +42 -0
  102. httpp-0.7.0/tests/cpp/test_client_async_engine.cpp +358 -0
  103. httpp-0.7.0/tests/cpp/test_client_session.cpp +211 -0
  104. httpp-0.7.0/tests/cpp/test_download.cpp +434 -0
  105. httpp-0.7.0/tests/cpp/test_log.cpp +133 -0
  106. httpp-0.7.0/tests/cpp/test_server.cpp +463 -0
  107. httpp-0.7.0/tests/cpp/test_server_features.cpp +234 -0
  108. httpp-0.7.0/tests/cpp/test_server_io_routes.cpp +316 -0
  109. httpp-0.7.0/tests/cpp/test_server_routing.cpp +225 -0
  110. httpp-0.7.0/tests/cpp/test_sha256.cpp +61 -0
  111. httpp-0.7.0/tests/cpp/test_swagger_ui.cpp +46 -0
  112. httpp-0.7.0/tests/cpp/test_swagger_ui_route.cpp +62 -0
  113. httpp-0.7.0/tests/cpp/tls_test_server.hpp +157 -0
  114. httpp-0.7.0/tests/python/test_async_engine.py +210 -0
  115. httpp-0.7.0/tests/python/test_client.py +305 -0
  116. httpp-0.7.0/tests/python/test_download_progress_url.py +302 -0
  117. httpp-0.7.0/tests/python/test_embed_file.py +119 -0
  118. httpp-0.7.0/tests/python/test_examples.py +37 -0
  119. {httpp-0.6.0 → httpp-0.7.0}/tests/python/test_httpp_basic.py +24 -0
  120. httpp-0.7.0/tests/python/test_httpp_routes.py +452 -0
  121. httpp-0.7.0/tests/python/test_log.py +100 -0
  122. httpp-0.7.0/tests/python/test_models_vs_pydantic.py +191 -0
  123. httpp-0.7.0/tests/python/test_openapi.py +300 -0
  124. httpp-0.7.0/tests/python/test_params_depends.py +387 -0
  125. httpp-0.7.0/tests/python/test_run.py +253 -0
  126. httpp-0.7.0/tests/python/test_server_features.py +887 -0
  127. httpp-0.7.0/tests/python/test_server_raw_request.py +225 -0
  128. httpp-0.7.0/tests/python/test_validate_vs_pydantic.py +134 -0
  129. httpp-0.7.0/tools/fastapi_probes/probe_fastapi_deps_responses.py +62 -0
  130. httpp-0.7.0/tools/fastapi_probes/probe_fastapi_params.py +68 -0
  131. httpp-0.7.0/tools/fastapi_probes/probe_pydantic_types.py +49 -0
  132. httpp-0.6.0/.github/workflows/wheels.yml +0 -72
  133. httpp-0.6.0/PKG-INFO +0 -186
  134. httpp-0.6.0/README.md +0 -161
  135. httpp-0.6.0/include/httpp/client.hpp +0 -124
  136. httpp-0.6.0/include/httpp/download.hpp +0 -84
  137. httpp-0.6.0/include/httpp/server.hpp +0 -50
  138. httpp-0.6.0/src/bindings/python/httpp_cy.pyx +0 -270
  139. httpp-0.6.0/src/core/client.cpp +0 -207
  140. httpp-0.6.0/src/core/download.cpp +0 -311
  141. httpp-0.6.0/src/core/server.cpp +0 -60
  142. httpp-0.6.0/src/core/url.cpp +0 -45
  143. httpp-0.6.0/src/third_party/README.md +0 -24
  144. httpp-0.6.0/tests/cpp/CMakeLists.txt +0 -27
  145. httpp-0.6.0/tests/cpp/test_download.cpp +0 -120
  146. httpp-0.6.0/tests/cpp/test_server.cpp +0 -74
  147. {httpp-0.6.0 → httpp-0.7.0}/.github/workflows/release_template.md +0 -0
  148. {httpp-0.6.0 → httpp-0.7.0}/.gitignore +0 -0
  149. {httpp-0.6.0 → httpp-0.7.0}/LICENSE.txt +0 -0
  150. {httpp-0.6.0 → httpp-0.7.0}/cmake/DynamicVersion.cmake +0 -0
  151. {httpp-0.6.0 → httpp-0.7.0}/cmake/FindCython.cmake +0 -0
  152. {httpp-0.6.0 → httpp-0.7.0}/cmake/Optimize.cmake +0 -0
  153. {httpp-0.6.0 → httpp-0.7.0}/cmake/Startup.cmake +0 -0
  154. {httpp-0.6.0 → httpp-0.7.0}/cmake/UseCython.cmake +0 -0
  155. {httpp-0.6.0 → httpp-0.7.0}/cmake/httppConfig.cmake.in +0 -0
  156. {httpp-0.6.0 → httpp-0.7.0}/docs/index.rst +0 -0
  157. {httpp-0.6.0 → httpp-0.7.0}/examples/find_httpp_via_python/CMakeLists.txt +0 -0
  158. {httpp-0.6.0 → httpp-0.7.0}/examples/find_httpp_via_python/main.cpp +0 -0
  159. {httpp-0.6.0 → httpp-0.7.0}/include/httpp/curl.hpp +0 -0
  160. {httpp-0.6.0 → httpp-0.7.0}/include/httpp/curl_compat.h +0 -0
  161. {httpp-0.6.0 → httpp-0.7.0}/include/httpp/export.hpp +0 -0
  162. {httpp-0.6.0 → httpp-0.7.0}/requirements-dev.txt +0 -0
  163. {httpp-0.6.0 → httpp-0.7.0}/src/core/curl.cpp +0 -0
  164. {httpp-0.6.0 → httpp-0.7.0}/src/core/curl_compat.cpp +0 -0
  165. {httpp-0.6.0 → httpp-0.7.0}/src/core/internal/httplib_common.hpp +0 -0
  166. {httpp-0.6.0 → httpp-0.7.0}/src/third_party/indicators/indicators.hpp +0 -0
  167. {httpp-0.6.0 → httpp-0.7.0}/tests/CMakeLists.txt +0 -0
  168. {httpp-0.6.0 → httpp-0.7.0}/tests/cmake/CMakeLists.txt +0 -0
  169. {httpp-0.6.0 → httpp-0.7.0}/tests/cmake/consumer_project/CMakeLists.txt +0 -0
  170. {httpp-0.6.0 → httpp-0.7.0}/tests/cmake/consumer_project/main.cpp +0 -0
  171. {httpp-0.6.0 → httpp-0.7.0}/tests/cmake/run_consumer_test.cmake +0 -0
  172. {httpp-0.6.0 → httpp-0.7.0}/tests/cpp/test_client.cpp +0 -0
  173. {httpp-0.6.0 → httpp-0.7.0}/tests/cpp/test_client_request.cpp +0 -0
  174. {httpp-0.6.0 → httpp-0.7.0}/tests/cpp/test_curl.cpp +0 -0
  175. {httpp-0.6.0 → httpp-0.7.0}/tests/cpp/test_curl_compat.cpp +0 -0
  176. {httpp-0.6.0 → httpp-0.7.0}/tests/cpp/test_progress.cpp +0 -0
  177. {httpp-0.6.0 → httpp-0.7.0}/tests/cpp/test_url.cpp +0 -0
  178. {httpp-0.6.0 → httpp-0.7.0}/tests/python/CMakeLists.txt +0 -0
  179. {httpp-0.6.0 → httpp-0.7.0}/tests/utest/utest.h +0 -0
  180. {httpp-0.6.0 → httpp-0.7.0}/tests/utest/utest_main.cxx +0 -0
  181. {httpp-0.6.0 → httpp-0.7.0}/version.py +0 -0
@@ -79,7 +79,7 @@ jobs:
79
79
  cmake -B build \
80
80
  -DHTTPP_BUILD_TESTS=ON \
81
81
  -DHTTPP_BUILD_PYTHON=ON \
82
- -DCMAKE_CXX_FLAGS="--coverage -O0 -g" \
82
+ -DCMAKE_CXX_FLAGS="--coverage -fprofile-update=atomic -O0 -g" \
83
83
  -DCMAKE_EXE_LINKER_FLAGS="--coverage"
84
84
 
85
85
  - name: Build and run C++ tests
@@ -67,8 +67,15 @@ jobs:
67
67
  path: dist
68
68
  merge-multiple: true
69
69
 
70
+ # Safety net: the publish steps need only flat *.whl / *.tar.gz files in dist/.
71
+ - name: Check dist layout
72
+ run: |
73
+ if find dist -mindepth 1 -type d | grep -q .; then
74
+ echo "::error::unexpected sub-directories in dist/"; find dist; exit 1
75
+ fi
76
+
70
77
  - name: List dist directory
71
- run: tree --du -shaC .
78
+ run: ls -lh dist
72
79
 
73
80
  - name: create release body
74
81
  uses: softprops/action-gh-release@v3
@@ -87,3 +94,7 @@ jobs:
87
94
  with:
88
95
  password: ${{ secrets.PYPI_PASSWORD }}
89
96
  user: ${{ secrets.PYPI_USERNAME }}
97
+ packages-dir: dist
98
+ skip-existing: true
99
+ # attestations need Trusted Publishing; they are ignored when a password is used
100
+ attestations: false
@@ -0,0 +1,103 @@
1
+ name: python build wheels
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ workflow_call:
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ env:
10
+ FORCE_COLOR: 3
11
+
12
+ concurrency:
13
+ group: wheels-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ jobs:
17
+ build_sdist:
18
+ name: Build SDist
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v6
22
+ with:
23
+ submodules: false
24
+
25
+ - uses: actions/setup-python@v6
26
+ with:
27
+ python-version: "3.11"
28
+ cache: "pip"
29
+
30
+ - name: Build SDist
31
+ run: pipx run build --sdist
32
+
33
+ - name: Check metadata
34
+ run: pipx run twine check dist/*
35
+
36
+ - uses: actions/upload-artifact@v7
37
+ with:
38
+ name: wheelhouse-sdist
39
+ path: dist/*.tar.gz
40
+ overwrite: true
41
+
42
+ build_wheels:
43
+ name: Wheels on ${{ matrix.os }}
44
+ runs-on: ${{ matrix.os }}
45
+ # A hung test used to keep this job running for the default 6 hours.
46
+ timeout-minutes: 90
47
+ strategy:
48
+ fail-fast: true
49
+ matrix:
50
+ os: [ubuntu-latest, macos-latest, windows-latest]
51
+
52
+ steps:
53
+ - uses: actions/checkout@v6
54
+ with:
55
+ submodules: recursive
56
+
57
+ - name: Build wheels
58
+ uses: pypa/cibuildwheel@v4.3.1
59
+ env:
60
+ MACOSX_DEPLOYMENT_TARGET: "10.13"
61
+ CIBW_ARCHS_MACOS: universal2
62
+ # Wheel layout (one build per platform for each row):
63
+ # cp39-abi3 every GIL-enabled Python from 3.9 up (the cp39-* selector;
64
+ # see py-api = "cp39" in pyproject.toml).
65
+ # cp313t / cp314t free-threaded 3.13 and 3.14: abi3 cannot be loaded by
66
+ # free-threaded builds, so each gets a version-specific
67
+ # wheel (this is where the no-GIL speed comes from).
68
+ # cp313t is built by the separate cibuildwheel 3.x step below,
69
+ # because cibuildwheel 4.x dropped 3.13 free-threading.
70
+ # cp315t free-threaded 3.15: abi3t (PEP 803, wheel.py-api =
71
+ # "cp315.cp315t" in scikit-build-core >= 1.0) would replace
72
+ # this and all later cpXXXt wheels, but it needs the PEP 793
73
+ # export hook, which released Cython versions do not generate
74
+ # yet. Until then 3.15t keeps a version-specific wheel.
75
+ #
76
+ # Free-threaded builds are enabled by default since cibuildwheel 4 (the old
77
+ # "cpython-freethreading" enable group no longer exists), and 3.15 is built
78
+ # without any prerelease flag.
79
+ CIBW_BUILD: "cp39-* cp314t-* cp315t-*"
80
+ # rpds-py (needed by jsonschema at build time) has no free-threaded wheel in its newest release; prefer an older
81
+ # binary over compiling the newest sdist with Rust.
82
+ CIBW_ENVIRONMENT: PIP_PREFER_BINARY=1
83
+
84
+ # cibuildwheel 4.x no longer supports free-threaded 3.13 (cp313t), so that one wheel
85
+ # comes from the 3.x series. Same wheelhouse/ folder: cibuildwheel never clears the
86
+ # output dir and the filenames differ by tag, so the artifact below stays flat.
87
+ - name: Build wheels (free-threaded 3.13)
88
+ uses: pypa/cibuildwheel@v3.4.1
89
+ env:
90
+ MACOSX_DEPLOYMENT_TARGET: "10.13"
91
+ CIBW_ARCHS_MACOS: universal2
92
+ CIBW_BUILD: "cp313t-*"
93
+ CIBW_ENABLE: cpython-freethreading
94
+ CIBW_ENVIRONMENT: PIP_PREFER_BINARY=1
95
+ CIBW_SKIP: pp*
96
+
97
+ - uses: actions/upload-artifact@v7
98
+ with:
99
+ name: wheelhouse-${{ matrix.os }}
100
+ # A single path keeps the artifact flat (several paths would preserve
101
+ # their parent folders and end up as dist/wheelhouse/... after download).
102
+ path: wheelhouse/*.whl
103
+ overwrite: false
@@ -4,3 +4,6 @@
4
4
  [submodule "src/third_party/liburlparser"]
5
5
  path = src/third_party/liburlparser
6
6
  url = https://github.com/mohammadraziei/liburlparser.git
7
+ [submodule "src/third_party/asio"]
8
+ path = src/third_party/asio
9
+ url = https://github.com/chriskohlhoff/asio.git
@@ -0,0 +1,198 @@
1
+ # Changelog
2
+
3
+ All notable changes to httpp are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project follows
5
+ [Semantic Versioning](https://semver.org/) (while the major version is 0, a minor release may still change an API;
6
+ such changes are listed under **Changed**).
7
+
8
+ ## [0.7.0] - 2026-10-17
9
+
10
+ The biggest release so far: a native async client and server, a FastAPI-style Python layer with OpenAPI and Swagger
11
+ UI, and a download that verifies, locks and times out.
12
+
13
+ ### Added
14
+
15
+ #### Client
16
+ - **Async client** (`Client.run_async()`, `AsyncClient`) running on the library's own non-blocking network thread
17
+ (Asio) with the vendored `llhttp` parser, instead of a shared thread pool. Keep-alive connection pooling and reuse
18
+ across requests, redirect handling, TLS and proxy support.
19
+ - **Streaming responses** and **structured errors** (`httpp.errors`, with an error kind such as `timeout`) for both
20
+ the blocking and the async client; extra URL helpers in `httpp/url.hpp`.
21
+
22
+ #### Server (C++ and Python)
23
+ - **Async HTTP/HTTPS server** (`async_server`, with a worker pool). TLS (Mbed TLS) with session caching: TLS contexts
24
+ for identical (verify, CA file) settings are built once and reused.
25
+ - **Python handlers:** route handlers with path parameters, `async def` handlers, and `listen()` that blocks and
26
+ returns a status (0.6.1); new in this release: `ResponseHead`, `file()`, `redirect()`, `set_cookie()`,
27
+ `ServerRequest.cookies`, streaming responses from generators and async generators, `@app.before_request`,
28
+ `@app.after_request`, error and exception handlers, startup and shutdown hooks, and `handler_timeout` (60 s by
29
+ default) that cancels a hanging async route and answers 504. Header values are checked against injection.
30
+ - **OpenAPI 3.1 and Swagger UI for Python.** Schema generation with Pydantic v2 models, per-route options (`summary`,
31
+ `tags`, `response_model`, ...), and `Query`, `Path`, `Header`, `Cookie`, `Body` and `Depends` (dependencies with their
32
+ own scopes and caching) that drive both the request handling and the generated document, so the docs cannot drift
33
+ from the behaviour.
34
+ - **`httpp::mount_swagger_ui(server, docs_path = "/docs", openapi_url = "/openapi.json", title = "API")`** in C++:
35
+ serves the page and the Swagger UI files (`swagger-ui-bundle.js`, `swagger-ui.css`, `swagger-api-icon.svg`) from
36
+ bytes compiled into the library, with `Cache-Control: public, max-age=86400`. Title and URL are escaped; an invalid
37
+ path throws `std::invalid_argument`. The Python server's `/docs` is now served from C++, without the GIL, and there
38
+ is no CDN or data directory involved.
39
+ - **`cmake/EmbedFile.cmake`** (`embed_files()`): compiles data files into a header at configure time (`constexpr`
40
+ data in `.rodata`, binary-safe, 32 bytes per source line).
41
+
42
+ #### Downloads
43
+ - **`download::sha256(hex)` and `download::sha256_url(url = "")`**: verify the finished file against a SHA-256 digest
44
+ (any letter case) or against a checksum file in `sha256sum` format (default `<url>.sha256`). The checksum file is
45
+ fetched first, so a missing or malformed one (an HTML error page, a name for another file, several lines) fails the
46
+ call before any download. A mismatch deletes the file, returns `ok = false` and says what the digest actually was.
47
+ With `resume()`, a complete-looking but corrupt file is downloaded again once from byte 0. `download_result` has a
48
+ new `sha256` field.
49
+ - **`httpp/sha256.hpp`**: `sha256_hex()`, `sha256_file()` (chunked) and `parse_sha256_checksum()`, on top of the
50
+ Mbed TLS already linked for HTTPS. About 165 MB/s on the machine it was measured on (130 MB file in 0.8 s).
51
+ - **`download::set_timeout(seconds)`**: connect, read and write timeouts of the transfer (like
52
+ `Client::set_timeout`), and also how long to wait for another process that holds the same file's lock.
53
+ - **`download::set_max_wait(milliseconds)`**: how long a lock that is *not being updated* is waited for before its
54
+ owner is taken for dead (default 2 s; the wait is `min(max_wait, timeout)`).
55
+ - **Cross-process lock, always on.** A download takes the directory `<dest>.lock` and its owner rewrites
56
+ `<dest>.lock/alive` every 200 ms at most; a lock that keeps being updated is waited for as long as it lives, one that
57
+ stops is taken over after `min(max_wait, timeout)`. A waiter that finds the file finished and matching `sha256()` /
58
+ `sha256_url()` uses it without a request (`skipped = true`). If the lock directory cannot be created the download
59
+ goes ahead unlocked. Written with `mkdir`/`rmdir` and plain files, no `<filesystem>`.
60
+
61
+ #### Library and tooling
62
+ - **Logging** through the vendored AixLog, with a `set_log_*` API in C, C++ and Python.
63
+ - **Python wheels:** one `cp39-abi3` wheel per platform for every GIL-enabled Python from 3.9 up, plus version-specific
64
+ free-threaded wheels (`cp313t`, `cp314t`, `cp315t`), because free-threaded builds cannot load abi3 wheels.
65
+ - **Documentation:** `docs/Architecture.md` (design, goals and test strategy), `docs/agent.md`, Python micro-benchmark
66
+ for the validators, and README sections for streaming, files, hooks, error handling and the client's new
67
+ capabilities.
68
+
69
+ ### Changed
70
+ - **Mbed TLS 4.2.0** (release tag), with threading support switched on (`MBEDTLS_THREADING_C`, via
71
+ `cmake/httpp_mbedtls_config.h`): pthread on POSIX, a small `SRWLOCK`/`CONDITION_VARIABLE` layer on Windows, or on
72
+ every platform with `-DHTTPP_MBEDTLS_THREADING_ALT=ON`. `MBEDTLS_SELF_TEST` is off.
73
+ - **`HTTPP_EMBED_AS_ARRAY` is `ON` on every platform** (it was MSVC only). One code path everywhere and no
74
+ `-Woverlength-strings` warning for the 1.5 MB Swagger UI bundle. Cost: compiling that translation unit takes about
75
+ 4.6 s instead of 0.9 s with GCC, once per build. `-DHTTPP_EMBED_AS_ARRAY=OFF` gives string literals again.
76
+ - The Swagger UI tab icon is a real SVG file served at `/docs/swagger-api-icon.svg`; the PNG favicon is gone.
77
+ - `<filesystem>` is not used any more (macOS 10.13 compatibility); `download.cpp` and the lock use `std::ifstream`,
78
+ `std::remove` and platform calls.
79
+ - Asio now drives both server loops and the async client (documentation and build comments updated).
80
+ - Build requirement: `scikit-build-core[pyproject]>=1.1`; the version is read through the standard
81
+ `[[tool.dynamic-metadata]]` table instead of the deprecated `tool.scikit-build.metadata`.
82
+ - A download creates (and removes again) `<dest>.lock` next to the destination file; the destination's directory must
83
+ be writable for the lock to work (otherwise the download is not locked).
84
+
85
+ ### Fixed
86
+ - Concurrent TLS handshakes shared Mbed TLS's global random generator without a lock; two handshakes could draw the
87
+ same random bytes. Found with ThreadSanitizer, fixed by the threading support above.
88
+ - Async `after`/`error` hooks, or a slow client, could block the event loop and deadlock concurrent requests; the
89
+ Python adapter now dispatches synchronous work directly when no async hook is registered.
90
+ - **Windows:** a finished download did not replace an existing file (`std::rename` fails when the target exists); it is
91
+ now an atomic `MoveFileExA(MOVEFILE_REPLACE_EXISTING)`. `ws2_32` is linked for `send()`/`WSAGetLastError()`, and the
92
+ `EmbedFile` generator no longer emits a zero-size array for an empty file (MSVC `C2466`).
93
+ - **macOS:** link the Keychain frameworks for the C++ tests; a URL-encoding test relied on `std::isalnum` being false
94
+ for non-ASCII bytes.
95
+ - Coverage builds use atomic profile counters (the server is multi-threaded, lcov saw negative counts); free-threaded
96
+ wheel builds prefer binary wheels so `rpds-py` is not built from source.
97
+ - Tests for concurrent async downloads measure event-loop ticks and overlapping streams instead of wall-clock time.
98
+ - **Release pipeline:** wheels are uploaded as one flat artifact, so `dist/` holds only `.whl` and `.tar.gz` files and
99
+ the PyPI upload no longer fails on a `wheelhouse` sub-directory. Windows wheels are not run through `delvewheel`
100
+ (`httpp_core.dll` is loaded from `httpp/lib/` at import time), and the PyPI step uses `skip-existing`.
101
+
102
+ ### Known limitations
103
+ - The async client has no timeout yet, and async hooks hold a thread while they run.
104
+ - `download::set_timeout`, `set_max_wait`, `sha256` and `sha256_url` are not exposed in the Python binding yet.
105
+ - A lock whose owner stops updating it for more than `max_wait` (heavy load, a suspended process) is taken over, and two
106
+ downloads can then run at once. Two waiters taking over the same dead lock in the same instant, or writing the same
107
+ destination, can write it together; use `sha256()` so a damaged result is caught and deleted. Keep `max_wait`
108
+ at 1 s or more.
109
+ - The Windows and macOS changes of this release were checked by CI only; the lock was not run under ThreadSanitizer.
110
+ - `/docs` has been looked at in Chromium only.
111
+
112
+ ## [0.6.1] - 2026-10-03
113
+
114
+ ### Added
115
+ - Python route handlers with path parameters and `async def` handlers; native async support for Python and C++
116
+ handlers.
117
+ - `listen()` blocks and returns a status in the C++ and Python bindings.
118
+ - Runnable standalone Python examples: server basics, routing, request and response handling, async handlers, static
119
+ files, error handling.
120
+
121
+ ### Changed
122
+ - Asio 1.34.2 is a git submodule (no system dependency, better scalability).
123
+ - README rewritten with a clearer description and structure; the Asio documentation was consolidated.
124
+
125
+ ### Fixed
126
+ - Windows: winsock type redefinition and missing socket headers in the tests; a Ctrl+C listener test that cannot run
127
+ there (Popen) is skipped and the SIGINT test was replaced with an `interrupt_main` variant.
128
+
129
+ ## [0.6.0] - 2026-09-16
130
+
131
+ ### Added
132
+ - Resumable downloads with skip-if-complete.
133
+
134
+ ### Changed
135
+ - **Breaking:** `download_file` was renamed to `download` and folded into one fluent builder.
136
+
137
+ ## [0.5.1] - 2026-09-16
138
+
139
+ ### Added
140
+ - `max_response_size` for the client and `follow_redirects` for downloads.
141
+ - `abi3` wheels in CI.
142
+
143
+ ### Changed
144
+ - `std::filesystem::remove` replaced by `std::remove` in downloads.
145
+
146
+ ## [0.5.0] - 2026-09-15
147
+
148
+ ### Added
149
+ - HTTPS support through Mbed TLS (documented in the API docs).
150
+
151
+ ### Changed
152
+ - Mbed TLS sources are generated before CMake configures; `jinja2` is a build dependency; submodules are no longer
153
+ fetched recursively by default.
154
+ - macOS: the Keychain frameworks are linked into `httpp_core`.
155
+ - Vendored-code warnings are suppressed and class-level exports avoided.
156
+ - Linux wheels are repaired with `auditwheel`.
157
+
158
+ ## [0.4.0] - 2026-09-14
159
+
160
+ ### Fixed
161
+ - Linux wheels are repaired with `auditwheel` in cibuildwheel builds.
162
+
163
+ ## [0.3.0] - 2026-09-14
164
+
165
+ Release only; no functional change recorded in the history.
166
+
167
+ ## [0.2.0] - 2026-09-13
168
+
169
+ ### Added
170
+ - curl-compatible API and CLI commands.
171
+
172
+ ## [0.1.1] - 2026-09-13
173
+
174
+ ### Fixed
175
+ - Linux wheels are repaired with `auditwheel` instead of copying libraries.
176
+
177
+ ## [0.1.0] - 2026-09-12
178
+
179
+ First release.
180
+
181
+ ### Added
182
+ - C++17 core: client, server and URL parsing (`liburlparser`, IPv4, IPv6 and Public Suffix List aware).
183
+ - Downloads with a progress bar (vendored `indicators`).
184
+ - Python package with `httpp_core` as a shared library bundled in the wheel; macOS rpath handling.
185
+ - CMake package: consumers find httpp through `httpp.get_cmake_dir()`; `HTTPP_BUILDING_DLL` for the shared core.
186
+ - Single umbrella header `include/httpp.h` that carries the version, and `version.py` for tagging and releases.
187
+ - CMake and wheels workflows in CI.
188
+
189
+ [0.7.0]: https://github.com/MohammadRaziei/httpp/compare/v0.6.1...v0.7.0
190
+ [0.6.1]: https://github.com/MohammadRaziei/httpp/compare/v0.6.0...v0.6.1
191
+ [0.6.0]: https://github.com/MohammadRaziei/httpp/compare/v0.5.1...v0.6.0
192
+ [0.5.1]: https://github.com/MohammadRaziei/httpp/compare/v0.5.0...v0.5.1
193
+ [0.5.0]: https://github.com/MohammadRaziei/httpp/compare/v0.4.0...v0.5.0
194
+ [0.4.0]: https://github.com/MohammadRaziei/httpp/compare/v0.3.0...v0.4.0
195
+ [0.3.0]: https://github.com/MohammadRaziei/httpp/compare/v0.2.0...v0.3.0
196
+ [0.2.0]: https://github.com/MohammadRaziei/httpp/compare/v0.1.1...v0.2.0
197
+ [0.1.1]: https://github.com/MohammadRaziei/httpp/compare/v0.1.0...v0.1.1
198
+ [0.1.0]: https://github.com/MohammadRaziei/httpp/releases/tag/v0.1.0
@@ -34,6 +34,20 @@ set(HTTPP_SOURCE_DIR ${PROJECT_SOURCE_DIR})
34
34
  # they never leak into the httpp .so/.a's public symbols.
35
35
  file(GLOB HTTPP_CORE_SOURCES ${PROJECT_SOURCE_DIR}/src/core/*.cpp)
36
36
 
37
+ # Swagger UI (vendored in src/third_party/swagger-ui) is compiled into the library: at CONFIGURE time its three
38
+ # files become a generated header (cmake/EmbedFile.cmake), so /docs needs no data files at run time.
39
+ include(EmbedFile)
40
+ set(HTTPP_GENERATED_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated)
41
+ set(HTTPP_SWAGGER_UI_DIR ${PROJECT_SOURCE_DIR}/src/third_party/swagger-ui)
42
+ embed_files(
43
+ OUTPUT ${HTTPP_GENERATED_DIR}/httpp_swagger_ui_data.hpp
44
+ NAMESPACE httpp::embedded
45
+ FILES
46
+ swagger_ui_bundle_js ${HTTPP_SWAGGER_UI_DIR}/swagger-ui-bundle.js
47
+ swagger_ui_css ${HTTPP_SWAGGER_UI_DIR}/swagger-ui.css
48
+ swagger_ui_icon_svg ${HTTPP_SWAGGER_UI_DIR}/swagger-api-icon.svg
49
+ )
50
+
37
51
  # liburlparser (git submodule, built from source — no system package) does
38
52
  # the real URL parsing (scheme/host/port/path, IPv4/IPv6, PSL-aware). It is
39
53
  # linked PRIVATE into httpp_core, same as cpp-httplib: consumers of
@@ -84,8 +98,38 @@ endif()
84
98
  # httpp links it in statically and never re-exports its headers/libs, so
85
99
  # nothing from mbedtls itself belongs in the installed wheel.
86
100
  set(DISABLE_PACKAGE_CONFIG_AND_INSTALL ON CACHE BOOL "" FORCE)
101
+ # Thread safety of Mbed TLS: its process-wide state (PSA random generator, key slots) is only locked when the
102
+ # threading layer is enabled, and httpp does TLS on many threads. See cmake/httpp_mbedtls_config.h.
103
+ option(HTTPP_MBEDTLS_THREADING_ALT "Use the alt threading layer (always on for Windows) instead of pthread" OFF)
104
+ if(HTTPP_MBEDTLS_THREADING_ALT)
105
+ add_compile_definitions(HTTPP_MBEDTLS_THREADING_ALT)
106
+ endif()
107
+ set(TF_PSA_CRYPTO_USER_CONFIG_FILE ${PROJECT_SOURCE_DIR}/cmake/httpp_mbedtls_config.h CACHE FILEPATH "" FORCE)
108
+ find_package(Threads REQUIRED)
109
+ set(LINK_WITH_PTHREAD ON CACHE BOOL "" FORCE)
110
+ # threading_alt.h (the alt layer's types) must be found by every Mbed TLS source (it builds several object libraries
111
+ # that do not see each other's include paths) and by everything that includes its headers: a directory-level include
112
+ # path, inherited by the subdirectories added below, reaches all of them.
113
+ if(WIN32 OR HTTPP_MBEDTLS_THREADING_ALT)
114
+ include_directories(${PROJECT_SOURCE_DIR}/src/core/mbedtls_alt)
115
+ endif()
87
116
  add_subdirectory(${PROJECT_SOURCE_DIR}/src/third_party/mbedtls)
88
117
 
118
+ # llhttp (vendored release C sources, kept in src/ not third_party/: it is an interim parser for the async client and goes away with cpp-httplib, no Node needed): the HTTP/1.x parser for
119
+ # the async client. Own C static target so the generated code is built without
120
+ # httpp's -Wall -Wextra -Wpedantic; see src/llhttp/VENDORED.md.
121
+ enable_language(C)
122
+ add_library(httpp_llhttp STATIC
123
+ ${PROJECT_SOURCE_DIR}/src/llhttp/src/llhttp.c
124
+ ${PROJECT_SOURCE_DIR}/src/llhttp/src/api.c
125
+ ${PROJECT_SOURCE_DIR}/src/llhttp/src/http.c)
126
+ target_include_directories(httpp_llhttp PUBLIC
127
+ ${PROJECT_SOURCE_DIR}/src/llhttp/include)
128
+ set_target_properties(httpp_llhttp PROPERTIES
129
+ LINKER_LANGUAGE C POSITION_INDEPENDENT_CODE ON C_VISIBILITY_PRESET hidden)
130
+ target_compile_options(httpp_llhttp PRIVATE
131
+ $<IF:$<C_COMPILER_ID:MSVC>,/w,-w>)
132
+
89
133
  add_library(httpp_core SHARED ${HTTPP_CORE_SOURCES})
90
134
  add_library(httpp::core ALIAS httpp_core)
91
135
 
@@ -116,14 +160,16 @@ target_include_directories(httpp_core
116
160
  # warnings (e.g. indicators.hpp's narrowing-conversion C4244/C4267 on MSVC)
117
161
  # aren't ours to fix and shouldn't surface under our own -Wall/-Wextra/-W4
118
162
  # below — without patching vendored code to silence its own warnings.
163
+ target_include_directories(httpp_core PRIVATE ${HTTPP_GENERATED_DIR})
119
164
  target_include_directories(httpp_core SYSTEM PRIVATE
120
165
  ${CMAKE_CURRENT_SOURCE_DIR}/src/third_party/cpp-httplib
166
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/third_party/aixlog
121
167
  ${CMAKE_CURRENT_SOURCE_DIR}/src/third_party
122
168
  )
123
169
 
124
170
  find_package(Threads REQUIRED)
125
171
  target_link_libraries(httpp_core PUBLIC Threads::Threads)
126
- target_link_libraries(httpp_core PRIVATE url::base)
172
+ target_link_libraries(httpp_core PRIVATE url::base httpp_llhttp)
127
173
 
128
174
  # HTTPS support: mbedtls is linked PRIVATE and CPPHTTPLIB_MBEDTLS_SUPPORT is
129
175
  # only ever defined in src/core/internal/httplib_common.hpp (never in a
@@ -131,6 +177,53 @@ target_link_libraries(httpp_core PRIVATE url::base)
131
177
  # include/link path — see that file for the shared httplib.h include.
132
178
  target_link_libraries(httpp_core PRIVATE mbedtls mbedx509)
133
179
  target_compile_definitions(httpp_core PRIVATE CPPHTTPLIB_MBEDTLS_SUPPORT)
180
+ # Asio (standalone, header-only, vendored) runs the async engines: the server's
181
+ # connection loop and the non-blocking client (epoll on Linux, kqueue on macOS,
182
+ # IOCP on Windows). It is only ever included from src/core/async_server.cpp and
183
+ # src/core/async_http.cpp, never together with httplib.h.
184
+ # Where Asio comes from, in order:
185
+ # 1. -DHTTPP_ASIO_INCLUDE_DIR=<directory containing asio.hpp> (offline builds, packagers)
186
+ # 2. src/third_party/asio, if it holds a copy: a git submodule (<dir>/asio/include)
187
+ # or a plain copy (<dir>/include)
188
+ # 3. downloaded at configure time, pinned by version and SHA-256 (about 3 MB)
189
+ set(HTTPP_ASIO_VERSION 1.34.2)
190
+ set(HTTPP_ASIO_SHA256 f3bac015305fbb700545bd2959fbc52d75a1ec2e05f9c7f695801273ceb78cf5)
191
+ set(HTTPP_ASIO_INCLUDE_DIR "" CACHE PATH "Directory containing asio.hpp (empty: auto-detect, else download)")
192
+ if(NOT HTTPP_ASIO_INCLUDE_DIR)
193
+ foreach(_candidate
194
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/third_party/asio/asio/include
195
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/third_party/asio/include)
196
+ if(EXISTS "${_candidate}/asio.hpp")
197
+ set(HTTPP_ASIO_INCLUDE_DIR "${_candidate}" CACHE PATH "" FORCE)
198
+ break()
199
+ endif()
200
+ endforeach()
201
+ endif()
202
+ if(NOT HTTPP_ASIO_INCLUDE_DIR)
203
+ include(FetchContent)
204
+ if(POLICY CMP0135)
205
+ cmake_policy(SET CMP0135 NEW) # extracted files get the time of extraction
206
+ endif()
207
+ string(REPLACE "." "-" _asio_tag "asio-${HTTPP_ASIO_VERSION}")
208
+ FetchContent_Declare(httpp_asio
209
+ URL https://codeload.github.com/chriskohlhoff/asio/tar.gz/refs/tags/${_asio_tag}
210
+ URL_HASH SHA256=${HTTPP_ASIO_SHA256})
211
+ FetchContent_MakeAvailable(httpp_asio) # Asio has no CMakeLists.txt: this only downloads and extracts
212
+ set(HTTPP_ASIO_INCLUDE_DIR "${httpp_asio_SOURCE_DIR}/asio/include" CACHE PATH "" FORCE)
213
+ endif()
214
+ message(STATUS "Asio ${HTTPP_ASIO_VERSION}: ${HTTPP_ASIO_INCLUDE_DIR}")
215
+
216
+ target_include_directories(httpp_core SYSTEM PRIVATE ${HTTPP_ASIO_INCLUDE_DIR})
217
+ target_compile_definitions(httpp_core PRIVATE ASIO_STANDALONE ASIO_NO_DEPRECATED)
218
+ if(WIN32)
219
+ # Asio wants the target Windows version spelled out (Windows 10 and later).
220
+ target_compile_definitions(httpp_core PRIVATE _WIN32_WINNT=0x0A00)
221
+ target_link_libraries(httpp_core PRIVATE ws2_32 mswsock crypt32)
222
+ endif()
223
+ if(MSVC)
224
+ # Asio's templates produce very large object files.
225
+ set_source_files_properties(${PROJECT_SOURCE_DIR}/src/core/async_http.cpp ${PROJECT_SOURCE_DIR}/src/core/async_server.cpp PROPERTIES COMPILE_OPTIONS "/bigobj")
226
+ endif()
134
227
 
135
228
  # macOS: cpp-httplib (src/third_party/cpp-httplib/httplib.h) reads the
136
229
  # system trust store via the Keychain APIs (CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN),