diff --git a/.clang-format b/.clang-format
index 5dee993..4b2b501 100644
--- a/.clang-format
+++ b/.clang-format
@@ -9,9 +9,11 @@ AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
-ColumnLimit: 150
+ColumnLimit: 0
PointerAlignment: Left
SpaceBeforeParens: ControlStatements
-SortIncludes: false
\ No newline at end of file
+SortIncludes: false
+MaxEmptyLinesToKeep: 1
+SeparateDefinitionBlocks: Always
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 99c5c04..3146c7f 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -14,8 +14,6 @@ body:
label: Operating system
options:
- Linux
- - Windows
- - macOS
- Other
validations:
required: true
@@ -33,7 +31,7 @@ body:
id: compiler
attributes:
label: Compiler
- placeholder: gcc 13, MSVC 2022, clang 18, etc.
+ placeholder: gcc 13, clang 18, etc.
validations:
required: true
diff --git a/.github/actions/build-linux-packages/action.yml b/.github/actions/build-linux-packages/action.yml
index 9342dd1..9cd88c8 100644
--- a/.github/actions/build-linux-packages/action.yml
+++ b/.github/actions/build-linux-packages/action.yml
@@ -32,7 +32,7 @@ runs:
bash -euc '
export DEBIAN_FRONTEND=noninteractive
apt-get update
- apt-get install -y --no-install-recommends build-essential libcjson-dev zlib1g-dev
+ apt-get install -y --no-install-recommends build-essential libcjson-dev zlib1g-dev libnghttp2-dev
make clean
make libchttpx.so
cp libchttpx.so dist/deb/libchttpx.so
@@ -47,7 +47,7 @@ runs:
-w /work \
fedora:42 \
bash -euc '
- dnf install -y gcc make cjson-devel zlib-devel
+ dnf install -y gcc make cjson-devel zlib-devel libnghttp2-devel
make clean
make libchttpx.so
cp libchttpx.so dist/rpm/libchttpx.so
@@ -62,7 +62,7 @@ runs:
-w /work \
archlinux:base-devel \
bash -euc '
- pacman -Sy --noconfirm cjson zlib
+ pacman -Sy --noconfirm cjson zlib libnghttp2
make clean
make libchttpx.so
cp libchttpx.so dist/archlinux/libchttpx.so
@@ -77,7 +77,7 @@ runs:
-w /work \
alpine:3.22 \
sh -euc '
- apk add --no-cache build-base cjson-dev zlib-dev
+ apk add --no-cache build-base cjson-dev zlib-dev nghttp2-dev
make clean
make libchttpx.so
cp libchttpx.so dist/apk/libchttpx.so
@@ -106,10 +106,10 @@ runs:
"includedir=${prefix}/include",
"",
"Name: libchttpx",
- "Description: A compact cross-platform HTTP/1.1 server library for C/C++.",
+ "Description: A compact cross-platform HTTP/2 server library for C/C++.",
f"Version: {version}",
"Libs: -L${libdir} -lchttpx",
- "Libs.private: -lz",
+ "Libs.private: -lz -lnghttp2",
"Cflags: -I${includedir}/libchttpx",
"",
])
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 2825d54..54fd238 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -19,9 +19,12 @@ Use a **conventional commit** title — it becomes the release version on merge:
- [ ] PR title follows the table above
- [ ] Code builds on Linux (`make lin` and `make libchttpx.so`)
-- [ ] Code builds on Windows (`make win`) if applicable
-- [ ] Source is formatted (`make lin-format` or `clang-format`)
-- [ ] README updated if public API or usage changed
+- [ ] Code builds on Linux (`make lin`) if applicable
+- [ ] Source follows `.clang-format` and keeps logical blocks readable
+- [ ] ASan/UBSan pass; TSan passes for concurrency-sensitive changes
+- [ ] Parser/network changes include negative-path tests and fuzz coverage where applicable
+- [ ] Public API changes document ownership, lifetime, thread-safety, and compatibility impact
+- [ ] Documentation is updated when public API or observable behavior changes
## Related issues
diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml
index 301ca38..a19fb54 100644
--- a/.github/workflows/auto-release.yml
+++ b/.github/workflows/auto-release.yml
@@ -150,13 +150,11 @@ jobs:
netcorelink/libchttpx
- A powerful, cross-platform HTTP server library in C/C++ for building full-featured web servers
+ A powerful HTTP/2 server library in C/C++ for Linux
---
- > :warning: **Warning:** Use the `linux environment` for stable library operation.
-
## Linux native packages
Download the package for your distribution from this release, then install it with the system package manager:
@@ -172,7 +170,7 @@ jobs:
sudo dnf install ./libchttpx-dev-*.rpm
# Arch Linux
- sudo pacman -U ./libchttpx-dev-*.pkg.tar.zst
+ sudo pacman -U ./libchttpx-dev_*.pkg.tar.zst
# Alpine Linux
sudo apk add --allow-untrusted ./libchttpx-dev_*.apk
@@ -184,12 +182,6 @@ jobs:
curl -s https://raw.githubusercontent.com/netcorelink/libchttpx/main/scripts/install.sh | sudo sh
```
- ## Windows
-
- ```powershell
- iwr https://raw.githubusercontent.com/netcorelink/libchttpx/main/scripts/install.ps1 -UseBasicParsing | iex
- ```
-
- name: Checkout APT repository
uses: actions/checkout@v4
with:
@@ -264,4 +256,3 @@ jobs:
run: |
set -euo pipefail
gh workflow run pages.yml --ref main
-
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d519a8e..2c733ee 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -2,16 +2,63 @@ name: CI
on:
push:
- branches: [main, master]
+ branches: [main, master, develop]
pull_request:
- branches: [main, master, audit/core-safety-hardening]
+ branches: [main, master, develop, audit/core-safety-hardening]
permissions:
contents: read
jobs:
+ style:
+ name: Format and public naming
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install clang-format
+ run: sudo apt update && sudo apt install -y clang-format
+
+ - name: Check newly maintained runtime sources
+ run: clang-format --dry-run --Werror src/cHTTPX_worker.c src/cHTTPX_worker.h tests/fuzz_headers.c
+
+ - name: Reject legacy result names outside compatibility aliases
+ shell: bash
+ run: |
+ set -euo pipefail
+ matches="$(grep -R -nE '\\bCHTTPX_(OK|ERR_[A-Z0-9_]+)\\b' src include example tests 2>/dev/null || true)"
+ matches="$(printf '%s\n' "$matches" | grep -vE '^(src/cHTTPX_serv\.h|include/libchttpx\.h):[0-9]+:#define CHTTPX_(OK|ERR_[A-Z0-9_]+) ' || true)"
+ if [ -n "$matches" ]; then
+ echo "Use canonical cHTTPX_OK / cHTTPX_ERR_* result names:"
+ echo "$matches"
+ exit 1
+ fi
+
+ static-analysis:
+ name: Static analysis (cppcheck)
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install cppcheck
+ run: sudo apt update && sudo apt install -y cppcheck
+
+ - name: Run cppcheck
+ run: |
+ cppcheck \
+ --enable=warning,performance,portability \
+ --suppress=missingIncludeSystem \
+ --inline-suppr \
+ -I include \
+ -I src \
+ --error-exitcode=1 \
+ src/ include/ example/
+
build-linux:
- name: Build (Linux)
+ name: Build and test (Linux)
+ needs: [style, static-analysis]
runs-on: ubuntu-latest
steps:
- name: Checkout
@@ -20,7 +67,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt update
- sudo apt install -y gcc make libcjson-dev zlib1g-dev clang-format
+ sudo apt install -y gcc make libcjson-dev zlib1g-dev libnghttp2-dev
- name: Build binary
run: make lin
@@ -34,11 +81,69 @@ jobs:
- name: Run tests
run: make test
- - name: Run ASan and UBSan tests
+ sanitizers:
+ name: ASan, UBSan and TSan
+ needs: build-linux
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install dependencies
+ run: |
+ sudo apt update
+ sudo apt install -y gcc make libcjson-dev zlib1g-dev libnghttp2-dev
+
+ - name: Run ASan and UBSan
run: make test-sanitize
+ - name: Run ThreadSanitizer
+ run: make test-tsan
+
+ fuzz-smoke:
+ name: HTTP parser fuzz smoke
+ needs: build-linux
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install dependencies
+ run: |
+ sudo apt update
+ sudo apt install -y clang make libcjson-dev zlib1g-dev libnghttp2-dev
+
+ - name: Run fuzz smoke test
+ run: make test-fuzz
+
+ package-smoke:
+ name: Package validation
+ needs: build-linux
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install dependencies
+ run: |
+ sudo apt update
+ sudo apt install -y gcc make libcjson-dev zlib1g-dev libnghttp2-dev
+
+ - name: Build release archive
+ run: make lin-lib
+
+ - name: Validate archive contents
+ shell: bash
+ run: |
+ set -euo pipefail
+ test -f libchttpx-dev.tar.gz
+ tar -tzf libchttpx-dev.tar.gz > /tmp/libchttpx-package-files.txt
+ grep -q '^libchttpx-dev/include/libchttpx.h$' /tmp/libchttpx-package-files.txt
+ grep -q '^libchttpx-dev/libchttpx.so$' /tmp/libchttpx-package-files.txt
+
build-linux-tls:
name: Build + HTTPS integration (Linux, OpenSSL)
+ needs: style
runs-on: ubuntu-latest
steps:
- name: Checkout
@@ -47,7 +152,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt update
- sudo apt install -y gcc make libcjson-dev zlib1g-dev libssl-dev openssl
+ sudo apt install -y gcc make libcjson-dev zlib1g-dev libnghttp2-dev libnghttp2-dev libssl-dev openssl
- name: Build TLS-enabled shared library
run: make TLS=1 libchttpx.so
@@ -60,6 +165,7 @@ jobs:
build-linux-compression:
name: Response compression integration (Linux, zlib)
+ needs: style
runs-on: ubuntu-latest
steps:
- name: Checkout
@@ -68,7 +174,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt update
- sudo apt install -y gcc make libcjson-dev zlib1g-dev libssl-dev
+ sudo apt install -y gcc make libcjson-dev zlib1g-dev libnghttp2-dev libnghttp2-dev libssl-dev
- name: Build shared library
run: make libchttpx.so
@@ -83,50 +189,3 @@ jobs:
run: |
make clean
make TLS=1 libchttpx.so
-
- build-windows:
- name: Build (Windows)
- runs-on: windows-latest
- defaults:
- run:
- shell: msys2 {0}
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Setup MSYS2
- uses: msys2/setup-msys2@v2
- with:
- msystem: MINGW64
- update: true
- install: >-
- mingw-w64-x86_64-gcc
- mingw-w64-x86_64-make
- mingw-w64-x86_64-zlib
-
- - name: Build
- run: mingw32-make win SHELL=cmd
-
- - name: Run tests
- run: mingw32-make test-win SHELL=cmd
-
- static-analysis:
- name: Static analysis (cppcheck)
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Install cppcheck
- run: sudo apt update && sudo apt install -y cppcheck
-
- - name: Run cppcheck
- run: |
- cppcheck \
- --enable=warning,performance,portability \
- --suppress=missingIncludeSystem \
- --inline-suppr \
- -I include \
- -I src \
- --error-exitcode=1 \
- src/ include/ example/
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 168e1e0..00990fe 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -33,7 +33,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt update
- sudo apt install -y gcc make libcjson-dev zlib1g-dev
+ sudo apt install -y gcc make libcjson-dev zlib1g-dev libnghttp2-dev
- name: Build
run: make libchttpx.so
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 579324c..df4e405 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -53,13 +53,11 @@ jobs:
netcorelink/libchttpx
- A powerful, cross-platform HTTP server library in C/C++ for building full-featured web servers
+ A powerful HTTP/2 server library in C/C++ for Linux
---
- > :warning: **Warning:** Use the `linux environment` for stable library operation.
-
## Linux native packages
Download the package for your distribution from this release, then install it with the system package manager:
@@ -87,12 +85,6 @@ jobs:
curl -s https://raw.githubusercontent.com/netcorelink/libchttpx/main/scripts/install.sh | sudo sh
```
- ## Windows
-
- ```powershell
- iwr https://raw.githubusercontent.com/netcorelink/libchttpx/main/scripts/install.ps1 -UseBasicParsing | iex
- ```
-
- name: Checkout APT repository
uses: actions/checkout@v4
with:
@@ -167,4 +159,3 @@ jobs:
run: |
set -euo pipefail
gh workflow run pages.yml --ref main
-
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..1d38c81
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,90 @@
+# Contributing to libchttpx
+
+Thank you for contributing to libchttpx. Changes to the HTTP parser, network runtime, TLS, memory ownership, public API, and concurrency model are treated as security- or stability-sensitive.
+
+## Pull request baseline
+
+A pull request must not be merged when it:
+
+- adds or changes public API without tests and documentation;
+- changes ownership or lifetime rules without documenting who owns the resource and when it becomes invalid;
+- changes public structure layout or function signatures without an API/ABI compatibility note;
+- introduces blocking socket I/O into the application worker pool;
+- adds unbounded queues, buffers, or client-controlled allocations;
+- ignores a system or library error that can affect correctness;
+- adds library-side `exit()` or `abort()`;
+- introduces mutable global state without an explicit synchronization and ownership model;
+- mixes unrelated refactoring with a feature or bug fix;
+- changes parser/network/TLS behavior without negative-path tests.
+
+## Runtime model
+
+The server runtime owns sockets and network readiness. Application workers execute middleware, routing, handlers, validation, and application code only.
+
+The worker pool has a fixed size of 32 threads. It is intentionally not exposed as a public configuration option. Work is submitted through a bounded queue and overload must fail predictably instead of growing memory without a limit.
+
+Routes, middleware, and server configuration are expected to be registered before the server starts. After startup they are treated as immutable unless an API explicitly documents otherwise.
+
+## Public naming
+
+Public functions and public result values use the `cHTTPX_` prefix.
+
+Canonical result values are:
+
+`cHTTPX_OK`, `cHTTPX_ERR_MEMORY`, `cHTTPX_ERR_SOCKET`, `cHTTPX_ERR_BIND`, `cHTTPX_ERR_LISTEN`, `cHTTPX_ERR_INVALID_ARGUMENT`, `cHTTPX_ERR_LIMIT`, `cHTTPX_ERR_IO`, `cHTTPX_ERR_NOT_FOUND`, `cHTTPX_ERR_PROTOCOL`, `cHTTPX_ERR_STATE`, `cHTTPX_ERR_UNAVAILABLE`, `cHTTPX_ERR_TIMEOUT`, `cHTTPX_ERR_TLS`, and `cHTTPX_ERR_COMPRESSION`.
+
+Legacy `CHTTPX_*` result names remain compatibility aliases for existing applications. New code and documentation must use `cHTTPX_*`.
+
+## Ownership and cleanup
+
+Every public pointer-bearing API must document whether a pointer is borrowed or owned, who frees it, and how long it remains valid.
+
+Request-scoped allocations and deferred cleanups are released when the request is cleaned up unless explicitly detached. Response helpers that allocate response body data transfer ownership to the response object, which must be released with `cHTTPX_ResponseCleanup()`.
+
+Cleanup functions must be safe after partial initialization when the API documents that cleanup is allowed.
+
+## Error handling
+
+Functions returning `chttpx_error_t` use `cHTTPX_OK` on success and a negative `cHTTPX_ERR_*` value on failure.
+
+Pointer-returning functions use `NULL` for failure unless the API explicitly documents another contract. Avoid logging the same error on multiple internal layers; the layer with enough context to make the message useful should log it.
+
+## Formatting
+
+The repository uses `.clang-format`. Keep logical sections readable with blank lines between unrelated operations and between definition blocks. Do not compress multiple independent operations into visually dense blocks.
+
+Do not wrap function declarations or calls merely to reduce line length when the existing style keeps them readable.
+
+## Required checks
+
+Changes must pass:
+
+- formatting check;
+- Linux build and unit/integration tests;
+- Linux build/tests for behavioral changes;
+- ASan/UBSan;
+- TSan for concurrency-sensitive code;
+- parser fuzz smoke tests;
+- static analysis.
+
+Compiler warnings are treated as defects. Library code is built with `-Wall -Wextra -Wpedantic` where supported.
+
+## Parser and network changes
+
+Parser inputs are untrusted. Tests should cover malformed framing, duplicate/conflicting Content-Length, Transfer-Encoding conflicts, oversized headers/body, embedded control bytes, truncated input, partial I/O, timeout, disconnect, and chunked edge cases.
+
+Fuzz targets are part of the normal maintenance workflow and should be extended when new parsing surfaces are added.
+
+## Versioning
+
+libchttpx follows Semantic Versioning:
+
+- backward-compatible feature: MINOR;
+- backward-compatible fix: PATCH;
+- incompatible public API/ABI change: MAJOR.
+
+Use conventional PR titles so release automation can select the correct bump.
+
+## Performance claims
+
+Any published performance claim must include CPU, RAM, OS, compiler and flags, concurrency, payload size, TLS mode, keep-alive mode, requests/second, p50/p95/p99 latency, CPU utilization, and memory-per-connection where applicable.
diff --git a/ChangeLog b/ChangeLog
index bda2c4e..acb88b0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,7 @@
- Unreleased
+Replaced per-connection blocking socket handling with a non-blocking event-driven runtime. Linux uses epoll, macOS/BSD uses kqueue and Windows uses WSAPoll; complete requests are dispatched to a fixed internal 32-thread worker pool and responses return to the event loop for non-blocking writes. Worker count is intentionally not configurable through the public server config.
+
Added request-scoped memory and contexts, response ownership, config-based initialization, typed values, JSON binding and builder APIs, route groups and scoped middleware, multipart/form parsing, upload policies, request IDs, Accept-Language selection, callback logging, chunked request decoding, body limits, graceful shutdown, and expanded tests/documentation.
Fixed multipart uploads buffering the complete request in RAM, truncated fixed-length body acceptance, partial response sends, incorrect status reason phrases, OPTIONS fallthrough, temporary upload cleanup, unsafe query/cookie tokenization, current-client races and busy looping, router prefix leaks, fatal library exit calls, and unsafe signal/longjmp recovery.
diff --git a/Dockerfile b/Dockerfile
index f6231d2..e8ce96e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -5,6 +5,7 @@ RUN apt-get update \
build-essential \
libcjson-dev \
zlib1g-dev \
+ libnghttp2-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /src
@@ -20,6 +21,7 @@ RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libcjson1 \
zlib1g \
+ libnghttp2-14 \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /pkg/usr/local/lib/libchttpx.so /usr/local/lib/
diff --git a/Makefile b/Makefile
index 217d8fa..4f2d921 100644
--- a/Makefile
+++ b/Makefile
@@ -4,9 +4,9 @@ RELEASE_DIR = libchttpx-dev
TAR = $(RELEASE_DIR).tar.gz
CC = gcc
-CFLAGS = -Wall -Wextra -O2 -Iinclude -Isrc
+CFLAGS = -Wall -Wextra -Wpedantic -O2 -Iinclude -Isrc
-# Native TLS is opt-in so plain HTTP builds keep zero OpenSSL dependency.
+# Native TLS is opt-in so cleartext HTTP/2 builds keep zero OpenSSL dependency.
TLS ?= 0
TLS_CFLAGS =
TLS_LDFLAGS =
@@ -17,8 +17,8 @@ TLS_LDFLAGS += -lssl -lcrypto
endif
CFLAGS += $(TLS_CFLAGS)
-TARGET_DLL = libchttpx.dll
CLANG_FORMAT = clang-format
+FUZZ_CC ?= clang
OBJDIR = .out
BINDIR = .build
@@ -27,30 +27,30 @@ PREFIX ?= /usr/local
DESTDIR ?= pkg
PKGDIR ?= /pkg/usr/local
-WIN_LIB_DIR = tools
-
-LIN_LDFLAGS = -lcjson -lz $(TLS_LDFLAGS)
-WIN_LDFLAGS = -lws2_32 -lz $(TLS_LDFLAGS)
+LIN_LDFLAGS = -lcjson -lz -lnghttp2 $(TLS_LDFLAGS)
TEST_TARGET = $(BINDIR)/test_core
TEST_SERVER_TARGET = $(BINDIR)/test_server
TEST_SANITIZE_TARGET = $(BINDIR)/test_core_sanitize
TEST_SERVER_SANITIZE_TARGET = $(BINDIR)/test_server_sanitize
+TEST_TSAN_TARGET = $(BINDIR)/test_server_tsan
+FUZZ_HEADERS_TARGET = $(BINDIR)/fuzz_headers
TEST_TLS_TARGET = $(BINDIR)/test_tls
TEST_COMPRESSION_TARGET = $(BINDIR)/test_compression
TEST_METRICS_TARGET = $(BINDIR)/test_metrics
+TEST_WEBSOCKET_TARGET = $(BINDIR)/test_websocket
+TEST_SSE_TARGET = $(BINDIR)/test_sse
EXAMPLE_SRC = example/basic.c
EXAMPLE_OBJ = $(OBJDIR)/example/basic.o
-EXAMPLE_NAMES = basic multiple_servers local_call remote_call middleware json upload metrics
+EXAMPLE_NAMES = basic multiple_servers local_call remote_call middleware json upload metrics websocket sse
EXAMPLE_TARGETS = $(addprefix $(BINDIR)/example-,$(EXAMPLE_NAMES))
LIN_SRCS = $(wildcard src/*.c)
-WIN_SRCS = $(wildcard src/*.c) lib/cjson/cJSON.c
+FORMAT_SRCS = $(wildcard src/*.c src/*.h include/*.h example/*.c tests/*.c)
LIN_OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(LIN_SRCS))
-WIN_OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(WIN_SRCS))
-# LINux build
+# Linux build
# -
lin: $(BINDIR)/$(TARGET)
@@ -77,20 +77,13 @@ examples-tls:
examples-compression: $(BINDIR)/example-compression
-# WINdows build
-# -
-
-win:
- if not exist $(BINDIR) mkdir $(BINDIR)
- $(CC) $(CFLAGS) -D_WIN32 -mconsole $(EXAMPLE_SRC) $(WIN_SRCS) -o $(BINDIR)/$(TARGET).exe $(WIN_LDFLAGS)
-
-# LINux shared library
+# Linux shared library
# -
libchttpx.so: $(LIN_OBJS)
$(CC) -shared -fPIC -o libchttpx.so $(LIN_OBJS) $(LIN_LDFLAGS) -pthread
-# LINux lib install
+# Linux lib install
# -
# before execution, you must run `make libchttpx.so`
@@ -106,38 +99,15 @@ lib-install: libchttpx.so
cp libchttpx.pc $(DESTDIR)$(PREFIX)/lib/pkgconfig/libchttpx.pc; \
fi
-# WINdows lib compile
-# -
-
-win-lib:
- @echo "Building Windows DLL..."
- if not exist $(BINDIR) mkdir $(BINDIR)
- $(CC) $(CFLAGS) -std=c11 -D_WIN32 -shared -o $(BINDIR)/$(TARGET_DLL) $(WIN_SRCS) -Wl,--out-implib,$(BINDIR)/libchttpx.a $(WIN_LDFLAGS)
-
- @echo "Copying files to $(WIN_LIB_DIR)..."
- if not exist $(WIN_LIB_DIR) mkdir $(WIN_LIB_DIR)
- copy /Y $(BINDIR)\$(TARGET_DLL) $(WIN_LIB_DIR)\$(TARGET_DLL)
- copy /Y $(BINDIR)\libchttpx.a $(WIN_LIB_DIR)\libchttpx.a
- for /f "delims=" %%i in ('where zlib1.dll 2^>nul') do copy /Y "%%i" $(WIN_LIB_DIR)\zlib1.dll
- if not exist $(WIN_LIB_DIR)\include mkdir $(WIN_LIB_DIR)\include
- xcopy /E /I /Y include $(WIN_LIB_DIR)\include
-
- @echo "Files copied to $(WIN_LIB_DIR) successfully!"
-
-test-win:
- if not exist $(BINDIR) mkdir $(BINDIR)
- $(CC) $(CFLAGS) -std=c11 -D_WIN32 tests/test_core.c src/*.c lib/cjson/cJSON.c -Wl,--stack,8388608 -o $(TEST_TARGET).exe $(WIN_LDFLAGS)
- $(TEST_TARGET).exe
- $(CC) $(CFLAGS) -std=c11 -D_WIN32 tests/test_server.c src/*.c lib/cjson/cJSON.c -Wl,--stack,8388608 -o $(TEST_SERVER_TARGET).exe $(WIN_LDFLAGS)
- $(TEST_SERVER_TARGET).exe
-
-# LINux tests
+# Linux tests
# -
-test: $(TEST_TARGET) $(TEST_SERVER_TARGET) $(TEST_METRICS_TARGET)
+test: $(TEST_TARGET) $(TEST_SERVER_TARGET) $(TEST_METRICS_TARGET) $(TEST_WEBSOCKET_TARGET) $(TEST_SSE_TARGET)
$(TEST_TARGET)
$(TEST_SERVER_TARGET)
$(TEST_METRICS_TARGET)
+ $(TEST_WEBSOCKET_TARGET)
+ $(TEST_SSE_TARGET)
$(TEST_TARGET): tests/test_core.c $(LIN_SRCS)
@mkdir -p $(BINDIR)
@@ -154,6 +124,16 @@ test-sanitize:
$(CC) $(CFLAGS) -std=gnu11 -O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined tests/test_server.c $(LIN_SRCS) -o $(TEST_SERVER_SANITIZE_TARGET) $(LIN_LDFLAGS) -pthread
ASAN_OPTIONS=detect_leaks=1 $(TEST_SERVER_SANITIZE_TARGET)
+test-tsan:
+ @mkdir -p $(BINDIR)
+ $(CC) $(CFLAGS) -std=gnu11 -O1 -g -fno-omit-frame-pointer -fsanitize=thread tests/test_server.c $(LIN_SRCS) -o $(TEST_TSAN_TARGET) $(LIN_LDFLAGS) -pthread
+ TSAN_OPTIONS=halt_on_error=1 $(TEST_TSAN_TARGET)
+
+test-fuzz:
+ @mkdir -p $(BINDIR)
+ $(FUZZ_CC) $(CFLAGS) -std=gnu11 -O1 -g -fno-omit-frame-pointer -fsanitize=fuzzer,address,undefined tests/fuzz_headers.c $(LIN_SRCS) -o $(FUZZ_HEADERS_TARGET) $(LIN_LDFLAGS) -pthread
+ $(FUZZ_HEADERS_TARGET) -runs=2000 -max_len=32768
+
test-tls:
@mkdir -p $(BINDIR)/tls
openssl req -x509 -newkey rsa:2048 -nodes -days 1 \
@@ -180,7 +160,15 @@ $(TEST_METRICS_TARGET): tests/test_metrics.c $(LIN_SRCS)
@mkdir -p $(BINDIR)
$(CC) $(CFLAGS) -std=gnu11 -O2 -g tests/test_metrics.c $(LIN_SRCS) -o $@ $(LIN_LDFLAGS) -pthread
-# LINux lib compile
+$(TEST_WEBSOCKET_TARGET): tests/test_websocket.c $(LIN_SRCS)
+ @mkdir -p $(BINDIR)
+ $(CC) $(CFLAGS) -std=gnu11 -O2 -g tests/test_websocket.c $(LIN_SRCS) -o $@ $(LIN_LDFLAGS) -pthread
+
+$(TEST_SSE_TARGET): tests/test_sse.c $(LIN_SRCS)
+ @mkdir -p $(BINDIR)
+ $(CC) $(CFLAGS) -std=gnu11 -g tests/test_sse.c $(LIN_SRCS) -o $@ $(LIN_LDFLAGS) -pthread
+
+# Linux lib compile
# -
lin-lib: clean libchttpx.so
@@ -205,35 +193,26 @@ lin-lib: clean libchttpx.so
@echo "Release package created: $(TAR)"
-# LINux run
+# Linux run
# -
lin-run: lin
$(BINDIR)/$(TARGET)
-# WINdows run
-# -
-
-win-run: win
- $(BINDIR)\$(TARGET).exe
-
-# LINux format
+# Linux format
# -
lin-format:
- @echo ">> Formatting clang source files"
- $(CLANG_FORMAT) -i $(LIN_SRCS)
-
-# WINdows format
-# -
+ @echo ">> Formatting C source and header files"
+ $(CLANG_FORMAT) -i $(FORMAT_SRCS)
-win-format:
- @echo ">> Formatting clang source files"
- $(CLANG_FORMAT) -i $(WIN_SRCS)
+format-check:
+ @echo ">> Checking clang-format"
+ $(CLANG_FORMAT) --dry-run --Werror $(FORMAT_SRCS)
-run: run-lin
+run: lin-run
clean:
- rm -rf $(OBJDIR) $(BINDIR) *.a *.dll
+ rm -rf $(OBJDIR) $(BINDIR) *.a
rm -rf $(RELEASE_DIR)
rm -rf libchttpx.so
diff --git a/README.md b/README.md
index f33b116..aa62588 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,12 @@
# libchttpx
-`libchttpx` is a compact cross-platform HTTP/1.1 server library for C. It provides an App-based runtime, routing, middleware, request parsing, JSON binding and responses, uploads, request-scoped memory, CORS, cookies, i18n, logging, rate limiting, and graceful shutdown while keeping a direct C-style API.
+`libchttpx` is a compact HTTP/2 server library for C on Linux. It provides an App-based runtime, routing, middleware, request parsing, JSON binding and responses, uploads, request-scoped memory, CORS, cookies, i18n, logging, rate limiting, and graceful shutdown while keeping a direct C-style API.
The library is designed so handlers contain application logic instead of repetitive HTTP plumbing.
## Highlights
-- Linux and Windows support
+- Linux support
- multiple independent HTTP servers inside one `cHTTPX_App`
- local direct server-to-server calls and remote HTTP/HTTPS calls
- route groups and `{parameter}` paths
@@ -19,9 +19,10 @@ The library is designed so handlers contain application logic instead of repetit
- CORS, cookies, logging callbacks, and rate limiting
- configurable gzip response compression with `Accept-Encoding` negotiation
- optional built-in metrics with thread-safe snapshots and a Prometheus exporter
+- first-class HTTP/2 Server-Sent Events with retry, heartbeat, and disconnect handling
- configurable server limits and graceful shutdown
-> `cHTTPX_ResFile()` currently reads the complete file into memory. Streaming responses, `sendfile()`, and zero-copy output are not implemented in the current API.
+> `cHTTPX_ResFile()` currently reads the complete file into memory. SSE has its own streaming HTTP/2 path; a generic streaming response API, `sendfile()`, and zero-copy file output are not implemented yet.
## Installation
@@ -89,24 +90,14 @@ sudo apk add --allow-untrusted ./libchttpx-dev_*.apk
Download the package for your distribution from [GitHub Releases](https://github.com/netcorelink/libchttpx/releases).
-### Legacy installer scripts
+### Legacy installer script
-The existing shell and PowerShell installers are kept as compatibility fallbacks.
-
-Linux:
+The existing shell installer is kept as a compatibility fallback:
```bash
curl -s https://raw.githubusercontent.com/netcorelink/libchttpx/main/scripts/install.sh | sudo sh
```
-Windows:
-
-```powershell
-iwr https://raw.githubusercontent.com/netcorelink/libchttpx/main/scripts/install.ps1 -UseBasicParsing | iex
-```
-
-Restart the terminal after the Windows installation so environment changes are visible.
-
### Docker
Pull the published runtime image:
@@ -115,7 +106,7 @@ Pull the published runtime image:
docker pull noneandundefined/libchttpx:latest
```
-The image contains the installed shared library, headers, pkg-config metadata, and the cJSON runtime.
+The image contains the installed shared library, headers, pkg-config metadata, and the cJSON, zlib, and nghttp2 runtimes.
```dockerfile
FROM noneandundefined/libchttpx:latest
@@ -126,11 +117,11 @@ CMD ["/usr/local/bin/my-server"]
### Build from source on Linux
-Requirements: GCC, Make, pkg-config, cJSON development files, and zlib development files.
+Requirements: GCC, Make, pkg-config, cJSON development files, zlib development files, and nghttp2 development files.
```bash
sudo apt update
-sudo apt install -y build-essential pkg-config libcjson-dev zlib1g-dev
+sudo apt install -y build-essential pkg-config libcjson-dev zlib1g-dev libnghttp2-dev
git clone https://github.com/netcorelink/libchttpx.git
cd libchttpx
@@ -164,20 +155,6 @@ make test-compression
TLS remains independently optional: `make TLS=1 libchttpx.so`. See [Response compression](docs/compression/README.md).
-### Build from source on Windows
-
-Use MinGW/GCC. The Windows build uses the bundled `lib/cjson` source and requires zlib (for MSYS2/MinGW64: `mingw-w64-x86_64-zlib`).
-
-```powershell
-git clone https://github.com/netcorelink/libchttpx.git
-cd libchttpx
-
-make win-lib
-make test-win
-```
-
-The generated DLL/import library and headers are copied into `tools/`.
-
## Documentation
Detailed documentation is split by functionality:
@@ -200,6 +177,7 @@ Detailed documentation is split by functionality:
- [Request IDs and i18n](docs/i18n/README.md)
- [Logging](docs/logging/README.md)
- [Rate limiting](docs/rate-limiting/README.md)
+- [Server-Sent Events](docs/sse/README.md)
- [WebSocket API — experimental](docs/websocket/README.md)
## License
diff --git a/README_RU.md b/README_RU.md
index 0d62dac..e782ca0 100644
--- a/README_RU.md
+++ b/README_RU.md
@@ -1,12 +1,12 @@
# libchttpx
-`libchttpx` — компактная кроссплатформенная HTTP/1.1-библиотека для C. Она предоставляет App-runtime, routing, middleware, разбор запросов, JSON binding/response, uploads, request-scoped память, CORS, cookies, i18n, logging, rate limiting и graceful shutdown, сохраняя простой C-style API.
+`libchttpx` — компактная HTTP/2-библиотека для C под Linux. Она предоставляет App-runtime, routing, middleware, разбор запросов, JSON binding/response, uploads, request-scoped память, CORS, cookies, i18n, logging, rate limiting и graceful shutdown, сохраняя простой C-style API.
Идея библиотеки: handler должен содержать бизнес-логику приложения, а не повторяющийся HTTP boilerplate.
## Основные возможности
-- Linux и Windows
+- поддержка Linux
- несколько независимых HTTP-серверов внутри одного `cHTTPX_App`
- прямые local-вызовы между серверами и remote-вызовы по HTTP/HTTPS
- route groups и пути с `{parameter}`
@@ -19,9 +19,10 @@
- CORS, cookies, callback-based logging и rate limiting
- настраиваемое gzip-сжатие ответов с `Accept-Encoding` negotiation
- опциональные встроенные metrics, thread-safe snapshot и Prometheus exporter
+- first-class Server-Sent Events по HTTP/2 с retry, heartbeat и disconnect handling
- лимиты сервера и graceful shutdown
-> `cHTTPX_ResFile()` пока полностью читает файл в память. Streaming response, `sendfile()` и zero-copy output в текущем API не реализованы.
+> `cHTTPX_ResFile()` пока полностью читает файл в память. Для SSE используется отдельный streaming path поверх HTTP/2; generic streaming response API, `sendfile()` и zero-copy для файлов пока не реализованы.
## Установка
@@ -89,31 +90,21 @@ sudo apk add --allow-untrusted ./libchttpx-dev_*.apk
Нужный пакет можно скачать из [GitHub Releases](https://github.com/netcorelink/libchttpx/releases).
-### Старые install-скрипты
+### Старый install-скрипт
-Bash- и PowerShell-скрипты пока остаются как запасной способ установки.
-
-Linux:
+Bash-скрипт остаётся как запасной способ установки:
```bash
curl -s https://raw.githubusercontent.com/netcorelink/libchttpx/main/scripts/install.sh | sudo sh
```
-Windows:
-
-```powershell
-iwr https://raw.githubusercontent.com/netcorelink/libchttpx/main/scripts/install.ps1 -UseBasicParsing | iex
-```
-
-После установки в Windows перезапустите терминал.
-
### Docker
```bash
docker pull noneandundefined/libchttpx:latest
```
-Image содержит установленную shared library, headers, pkg-config metadata и runtime cJSON.
+Image содержит установленную shared library, headers, pkg-config metadata и runtime-зависимости cJSON, zlib и nghttp2.
```dockerfile
FROM noneandundefined/libchttpx:latest
@@ -126,7 +117,7 @@ CMD ["/usr/local/bin/my-server"]
```bash
sudo apt update
-sudo apt install -y build-essential pkg-config libcjson-dev zlib1g-dev
+sudo apt install -y build-essential pkg-config libcjson-dev zlib1g-dev libnghttp2-dev
git clone https://github.com/netcorelink/libchttpx.git
cd libchttpx
@@ -160,20 +151,6 @@ make test-compression
TLS остаётся отдельной опцией сборки: `make TLS=1 libchttpx.so`. Подробнее: [Сжатие HTTP-ответов](docs/compression/README_RU.md).
-### Самостоятельная сборка на Windows
-
-Используется MinGW/GCC. Для Windows cJSON уже находится в `lib/cjson`, а zlib должен быть установлен (для MSYS2/MinGW64: `mingw-w64-x86_64-zlib`).
-
-```powershell
-git clone https://github.com/netcorelink/libchttpx.git
-cd libchttpx
-
-make win-lib
-make test-win
-```
-
-DLL, import library и headers копируются в `tools/`.
-
## Документация
Подробная документация разделена по функционалу:
@@ -196,9 +173,9 @@ DLL, import library и headers копируются в `tools/`.
- [Request ID и i18n](docs/i18n/README.md)
- [Logging](docs/logging/README_RU.md)
- [Rate limiting](docs/rate-limiting/README_RU.md)
+- [Server-Sent Events](docs/sse/README_RU.md)
- [WebSocket API — experimental](docs/websocket/README_RU.md)
-
## Лицензия
BSD 3-Clause. См. [LICENSE](LICENSE).
diff --git a/docs/README.md b/docs/README.md
index cfbb8af..5a08970 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -21,6 +21,7 @@ The root README contains only the project overview, installation methods, and na
| i18n | [i18n](i18n/README.md) | request IDs, `Accept-Language`, translations |
| Logging | [logging](logging/README.md) | logger callbacks and request logging middleware |
| Rate limiting | [rate-limiting](rate-limiting/README.md) | per-server fixed-window limiter |
+| Server-Sent Events | [sse](sse/README.md) | HTTP/2 event streams, retry, heartbeat, disconnect handling |
| WebSocket | [websocket](websocket/README.md) | current experimental API and limitations |
## Recommended reading order
@@ -39,3 +40,8 @@ Examples assume:
```
unless a module explicitly states otherwise.
+
+## Maintainer documentation
+
+- [Maintainer architecture baseline](maintainers/architecture.md) — runtime ownership, worker model, API/ABI policy, error model, CI gates, and benchmark protocol.
+- Repository contribution and merge requirements are defined in [`CONTRIBUTING.md`](../CONTRIBUTING.md).
diff --git a/docs/README_RU.md b/docs/README_RU.md
index fc584e6..6666725 100644
--- a/docs/README_RU.md
+++ b/docs/README_RU.md
@@ -21,6 +21,7 @@
| i18n | [i18n](i18n/README_RU.md) | request ID, `Accept-Language`, translations |
| Logging | [logging](logging/README_RU.md) | logger callback и logging middleware |
| Rate limiting | [rate-limiting](rate-limiting/README_RU.md) | встроенный fixed-window limiter |
+| Server-Sent Events | [sse](sse/README_RU.md) | HTTP/2 event streams, retry, heartbeat и disconnect handling |
| WebSocket | [websocket](websocket/README_RU.md) | текущий experimental API и ограничения |
## Рекомендуемый порядок
diff --git a/docs/app/README.md b/docs/app/README.md
index aaaae9c..0694d29 100644
--- a/docs/app/README.md
+++ b/docs/app/README.md
@@ -7,7 +7,7 @@
```c
chttpx_app_t app;
-if (cHTTPX_AppInit(&app) != CHTTPX_OK)
+if (cHTTPX_AppInit(&app) != cHTTPX_OK)
return 1;
```
@@ -40,7 +40,7 @@ Names must be unique across local and remote registrations.
## Start, wait, run
```c
-if (cHTTPX_AppStart(&app) != CHTTPX_OK)
+if (cHTTPX_AppStart(&app) != cHTTPX_OK)
{
cHTTPX_AppShutdown(&app);
return 1;
@@ -117,16 +117,16 @@ Connect/send/receive use a fixed 30-second timeout.
| Result | Meaning |
| --- | --- |
-| `CHTTPX_OK` | valid HTTP response received, including HTTP 4xx/5xx |
-| `CHTTPX_ERR_UNAVAILABLE` | DNS/connect failure or connection lost before a valid response |
-| `CHTTPX_ERR_TIMEOUT` | connect/send/receive exceeded 30 seconds |
-| `CHTTPX_ERR_TLS` | TLS handshake, certificate verification, encrypted read/write failure |
-| `CHTTPX_ERR_PROTOCOL` | peer returned invalid HTTP |
+| `cHTTPX_OK` | valid HTTP response received, including HTTP 4xx/5xx |
+| `cHTTPX_ERR_UNAVAILABLE` | DNS/connect failure or connection lost before a valid response |
+| `cHTTPX_ERR_TIMEOUT` | connect/send/receive exceeded 30 seconds |
+| `cHTTPX_ERR_TLS` | TLS handshake, certificate verification, encrypted read/write failure |
+| `cHTTPX_ERR_PROTOCOL` | peer returned invalid HTTP |
A 400/403/500 response means the server responded successfully at the transport layer:
```c
-if (result == CHTTPX_OK)
+if (result == cHTTPX_OK)
{
if (res->status >= 400)
{
@@ -150,7 +150,7 @@ static void create_payment(
res
);
- if (result == CHTTPX_ERR_UNAVAILABLE)
+ if (result == cHTTPX_ERR_UNAVAILABLE)
{
*res = cHTTPX_ResError(
cHTTPX_StatusServiceUnavailable,
@@ -159,7 +159,7 @@ static void create_payment(
return;
}
- if (result == CHTTPX_ERR_TIMEOUT)
+ if (result == cHTTPX_ERR_TIMEOUT)
{
*res = cHTTPX_ResError(
cHTTPX_StatusGatewayTimeout,
@@ -168,7 +168,7 @@ static void create_payment(
return;
}
- if (result != CHTTPX_OK)
+ if (result != cHTTPX_OK)
{
*res = cHTTPX_ResError(
cHTTPX_StatusBadGateway,
diff --git a/docs/app/README_RU.md b/docs/app/README_RU.md
index be390bf..c795ba4 100644
--- a/docs/app/README_RU.md
+++ b/docs/app/README_RU.md
@@ -7,7 +7,7 @@
```c
chttpx_app_t app;
-if (cHTTPX_AppInit(&app) != CHTTPX_OK)
+if (cHTTPX_AppInit(&app) != cHTTPX_OK)
return 1;
```
@@ -36,7 +36,7 @@ chttpx_serv_t* admin_server =
## Запуск
```c
-if (cHTTPX_AppStart(&app) != CHTTPX_OK)
+if (cHTTPX_AppStart(&app) != cHTTPX_OK)
{
cHTTPX_AppShutdown(&app);
return 1;
@@ -114,16 +114,16 @@ Timeout через `CallEx` менять нельзя.
| Результат | Значение |
| --- | --- |
-| `CHTTPX_OK` | получен корректный HTTP response, даже если status 400/403/500 |
-| `CHTTPX_ERR_UNAVAILABLE` | DNS/connect failed или connection оборвался до HTTP response |
-| `CHTTPX_ERR_TIMEOUT` | connect/send/receive превысил 30 секунд |
-| `CHTTPX_ERR_TLS` | ошибка TLS handshake, certificate verification или encrypted read/write |
-| `CHTTPX_ERR_PROTOCOL` | remote сторона вернула некорректный HTTP response |
+| `cHTTPX_OK` | получен корректный HTTP response, даже если status 400/403/500 |
+| `cHTTPX_ERR_UNAVAILABLE` | DNS/connect failed или connection оборвался до HTTP response |
+| `cHTTPX_ERR_TIMEOUT` | connect/send/receive превысил 30 секунд |
+| `cHTTPX_ERR_TLS` | ошибка TLS handshake, certificate verification или encrypted read/write |
+| `cHTTPX_ERR_PROTOCOL` | remote сторона вернула некорректный HTTP response |
HTTP 500 — не transport error:
```c
-if (result == CHTTPX_OK && res->status == 500)
+if (result == cHTTPX_OK && res->status == 500)
{
/* server жив, но его handler вернул ошибку */
}
@@ -144,7 +144,7 @@ static void create_payment(
res
);
- if (result == CHTTPX_ERR_UNAVAILABLE)
+ if (result == cHTTPX_ERR_UNAVAILABLE)
{
*res = cHTTPX_ResError(
cHTTPX_StatusServiceUnavailable,
@@ -153,7 +153,7 @@ static void create_payment(
return;
}
- if (result == CHTTPX_ERR_TIMEOUT)
+ if (result == cHTTPX_ERR_TIMEOUT)
{
*res = cHTTPX_ResError(
cHTTPX_StatusGatewayTimeout,
@@ -162,7 +162,7 @@ static void create_payment(
return;
}
- if (result != CHTTPX_OK)
+ if (result != cHTTPX_OK)
{
*res = cHTTPX_ResError(
cHTTPX_StatusBadGateway,
diff --git a/docs/compression/README.md b/docs/compression/README.md
index f400bcd..9d0036a 100644
--- a/docs/compression/README.md
+++ b/docs/compression/README.md
@@ -30,7 +30,7 @@ compression.min_size = 1024;
compression.level = 5;
int result = cHTTPX_CompressionUse(server, &compression);
-if (result != CHTTPX_OK) {
+if (result != cHTTPX_OK) {
/* handle configuration / provider error */
}
```
@@ -146,7 +146,7 @@ static int encode_zstd(
void *user_data)
{
/* allocate *output and encode the complete input */
- return CHTTPX_OK;
+ return cHTTPX_OK;
}
chttpx_compression_provider_t providers[] = {
@@ -168,7 +168,7 @@ Providers are evaluated using the client's quality values. When qualities tie, p
## Errors and logging
-Invalid configuration returns `CHTTPX_ERR_INVALID_ARGUMENT`; allocation failures return `CHTTPX_ERR_MEMORY`; gzip/provider failures use `CHTTPX_ERR_COMPRESSION` internally.
+Invalid configuration returns `cHTTPX_ERR_INVALID_ARGUMENT`; allocation failures return `cHTTPX_ERR_MEMORY`; gzip/provider failures use `cHTTPX_ERR_COMPRESSION` internally.
If encoding fails and identity is acceptable, libchttpx logs a warning and sends the original response. If identity is forbidden, it sends an empty `500 Internal Server Error` rather than silently violating `Accept-Encoding`.
diff --git a/docs/compression/README_RU.md b/docs/compression/README_RU.md
index 7d84056..71d69e7 100644
--- a/docs/compression/README_RU.md
+++ b/docs/compression/README_RU.md
@@ -30,7 +30,7 @@ compression.min_size = 1024;
compression.level = 5;
int result = cHTTPX_CompressionUse(server, &compression);
-if (result != CHTTPX_OK) {
+if (result != cHTTPX_OK) {
/* обработка ошибки */
}
```
@@ -146,7 +146,7 @@ static int encode_zstd(
void *user_data)
{
/* выделить *output и закодировать input */
- return CHTTPX_OK;
+ return cHTTPX_OK;
}
chttpx_compression_provider_t providers[] = {
@@ -168,7 +168,7 @@ Provider выбирается по quality клиента. При одинако
## Ошибки и logging
-Некорректная конфигурация возвращает `CHTTPX_ERR_INVALID_ARGUMENT`, ошибка памяти — `CHTTPX_ERR_MEMORY`, а ошибка compression provider — `CHTTPX_ERR_COMPRESSION` внутри middleware.
+Некорректная конфигурация возвращает `cHTTPX_ERR_INVALID_ARGUMENT`, ошибка памяти — `cHTTPX_ERR_MEMORY`, а ошибка compression provider — `cHTTPX_ERR_COMPRESSION` внутри middleware.
Если provider не смог сжать body, но клиент принимает identity, libchttpx пишет warning в logger и отправляет исходный ответ. Если identity запрещён, возвращается пустой `500 Internal Server Error`, а не ответ с неподдерживаемым encoding.
diff --git a/docs/cors/README.md b/docs/cors/README.md
index 95d6ee9..ac1649c 100644
--- a/docs/cors/README.md
+++ b/docs/cors/README.md
@@ -45,7 +45,7 @@ The server copies CORS configuration into its own state.
A CORS preflight looks like:
```http
-OPTIONS /api/v2/users HTTP/1.1
+OPTIONS /api/v2/users HTTP/2
Origin: https://example.com
Access-Control-Request-Method: POST
```
diff --git a/docs/cors/README_RU.md b/docs/cors/README_RU.md
index 637d70c..810fc8c 100644
--- a/docs/cors/README_RU.md
+++ b/docs/cors/README_RU.md
@@ -42,7 +42,7 @@ CORS config копируется в server state.
## Preflight
```http
-OPTIONS /api/v2/users HTTP/1.1
+OPTIONS /api/v2/users HTTP/2
Origin: https://example.com
Access-Control-Request-Method: POST
```
diff --git a/docs/json/README.md b/docs/json/README.md
index d63dd4e..71d3e16 100644
--- a/docs/json/README.md
+++ b/docs/json/README.md
@@ -38,7 +38,7 @@ static void create_user(
true,
3,
254,
- CHTTPX_TRIM | CHTTPX_LOWERCASE,
+ cHTTPX_TRIM | cHTTPX_LOWERCASE,
NULL
),
cHTTPX_StringField(
@@ -47,17 +47,20 @@ static void create_user(
true,
3,
32,
- CHTTPX_TRIM,
+ cHTTPX_TRIM,
validate_username
),
};
- if (!cHTTPX_BindJSON(
- req,
- res,
- fields,
- CHTTPX_ARRAY_LEN(fields)))
+ int bind = cHTTPX_BindJSON(req, fields, CHTTPX_ARRAY_LEN(fields));
+ if (bind != cHTTPX_BIND_OK)
+ {
+ if (bind == cHTTPX_BIND_REQUIRED)
+ *res = cHTTPX_ResError(cHTTPX_StatusBadRequest, req->error_field);
+ else
+ *res = cHTTPX_ResError(cHTTPX_StatusBadRequest, "invalid request body");
return;
+ }
*res = cHTTPX_ResMessage(
cHTTPX_StatusCreated,
@@ -66,13 +69,13 @@ static void create_user(
}
```
-On failure, `BindJSON` returns 0 and builds a safe HTTP 400 response.
+`BindJSON` does not write the HTTP response. `0` is success. On failure it returns a code such as `cHTTPX_BIND_REQUIRED` (`1`) and sets `req->error_field` / `req->error_num`.
## Normalizers
-- `CHTTPX_TRIM`
-- `CHTTPX_LOWERCASE`
-- `CHTTPX_UPPERCASE`
+- `cHTTPX_TRIM`
+- `cHTTPX_LOWERCASE`
+- `cHTTPX_UPPERCASE`
They can be OR-combined.
diff --git a/docs/json/README_RU.md b/docs/json/README_RU.md
index e38791a..c5755c0 100644
--- a/docs/json/README_RU.md
+++ b/docs/json/README_RU.md
@@ -24,7 +24,7 @@ static void create_user(
true,
3,
254,
- CHTTPX_TRIM | CHTTPX_LOWERCASE,
+ cHTTPX_TRIM | cHTTPX_LOWERCASE,
NULL
),
cHTTPX_StringField(
@@ -33,17 +33,20 @@ static void create_user(
true,
3,
32,
- CHTTPX_TRIM,
+ cHTTPX_TRIM,
NULL
),
};
- if (!cHTTPX_BindJSON(
- req,
- res,
- fields,
- CHTTPX_ARRAY_LEN(fields)))
+ int bind = cHTTPX_BindJSON(req, fields, CHTTPX_ARRAY_LEN(fields));
+ if (bind != cHTTPX_BIND_OK)
+ {
+ if (bind == cHTTPX_BIND_REQUIRED)
+ *res = cHTTPX_ResError(cHTTPX_StatusBadRequest, req->error_field);
+ else
+ *res = cHTTPX_ResError(cHTTPX_StatusBadRequest, "invalid request body");
return;
+ }
*res = cHTTPX_ResMessage(
cHTTPX_StatusCreated,
@@ -52,13 +55,13 @@ static void create_user(
}
```
-При ошибке `BindJSON` возвращает 0 и формирует безопасный 400 response.
+`BindJSON` сам HTTP-ответ не пишет. `0` — успех. При ошибке возвращается код, например `cHTTPX_BIND_REQUIRED` (`1`), плюс `req->error_field` / `req->error_num`.
## Normalizers
-- `CHTTPX_TRIM`
-- `CHTTPX_LOWERCASE`
-- `CHTTPX_UPPERCASE`
+- `cHTTPX_TRIM`
+- `cHTTPX_LOWERCASE`
+- `cHTTPX_UPPERCASE`
Можно комбинировать через OR.
diff --git a/docs/logging/README.md b/docs/logging/README.md
index 5fdcf86..d5dbdd0 100644
--- a/docs/logging/README.md
+++ b/docs/logging/README.md
@@ -30,17 +30,17 @@ cHTTPX_SetLogger(
server,
logger,
NULL,
- CHTTPX_LOG_INFO
+ cHTTPX_LOG_INFO
);
```
Levels:
-- `CHTTPX_LOG_DEBUG`
-- `CHTTPX_LOG_INFO`
-- `CHTTPX_LOG_WARN`
-- `CHTTPX_LOG_ERROR`
-- `CHTTPX_LOG_OFF`
+- `cHTTPX_LOG_DEBUG`
+- `cHTTPX_LOG_INFO`
+- `cHTTPX_LOG_WARN`
+- `cHTTPX_LOG_ERROR`
+- `cHTTPX_LOG_OFF`
`user_data` is passed back to the callback and can point to an application logging sink.
@@ -59,14 +59,14 @@ cHTTPX_SetLogger(
public_server,
public_logger,
public_sink,
- CHTTPX_LOG_INFO
+ cHTTPX_LOG_INFO
);
cHTTPX_SetLogger(
admin_server,
audit_logger,
audit_sink,
- CHTTPX_LOG_DEBUG
+ cHTTPX_LOG_DEBUG
);
```
diff --git a/docs/logging/README_RU.md b/docs/logging/README_RU.md
index 9696519..bc5f1c8 100644
--- a/docs/logging/README_RU.md
+++ b/docs/logging/README_RU.md
@@ -30,17 +30,17 @@ cHTTPX_SetLogger(
server,
logger,
NULL,
- CHTTPX_LOG_INFO
+ cHTTPX_LOG_INFO
);
```
Levels:
-- `CHTTPX_LOG_DEBUG`
-- `CHTTPX_LOG_INFO`
-- `CHTTPX_LOG_WARN`
-- `CHTTPX_LOG_ERROR`
-- `CHTTPX_LOG_OFF`
+- `cHTTPX_LOG_DEBUG`
+- `cHTTPX_LOG_INFO`
+- `cHTTPX_LOG_WARN`
+- `cHTTPX_LOG_ERROR`
+- `cHTTPX_LOG_OFF`
`user_data` можно использовать для передачи собственного logger/sink.
@@ -57,14 +57,14 @@ cHTTPX_SetLogger(
public_server,
public_logger,
public_sink,
- CHTTPX_LOG_INFO
+ cHTTPX_LOG_INFO
);
cHTTPX_SetLogger(
admin_server,
audit_logger,
audit_sink,
- CHTTPX_LOG_DEBUG
+ cHTTPX_LOG_DEBUG
);
```
diff --git a/docs/maintainers/architecture.md b/docs/maintainers/architecture.md
new file mode 100644
index 0000000..2666439
--- /dev/null
+++ b/docs/maintainers/architecture.md
@@ -0,0 +1,129 @@
+# Maintainer architecture baseline
+
+This document defines the architecture and maintenance constraints for libchttpx.
+
+## Server responsibilities
+
+`cHTTPX_serv.c` owns public server configuration, lifecycle, route registration, and shutdown coordination.
+
+`cHTTPX_runtime.c` owns connection lifecycle and the state machine. The runtime never exposes the event backend or scheduler through the public synchronous handler API.
+
+`cHTTPX_event.c` owns readiness notification and non-blocking socket registration.
+
+`cHTTPX_worker.c` owns the bounded application job queue and the fixed 32-thread worker pool.
+
+The intended flow is:
+
+```text
+listener / event loop
+ |
+ +-- non-blocking connection I/O
+ |
+ +-- HTTP framing
+ |
+ v
+ bounded job queue
+ |
+ v
+ 32 application workers
+ |
+ v
+ completed response queue
+ |
+ v
+ event loop
+ |
+ v
+ non-blocking socket write
+```
+
+Socket read/write ownership remains in the runtime/event loop. Workers execute request processing and application code and return completed response buffers.
+
+## Connection state machine
+
+Connections move through explicit states:
+
+- TLS handshake;
+- reading headers;
+- reading body;
+- processing;
+- writing;
+- closing.
+
+A connection is removed from readiness polling while application code is processing it. Completed responses are handed back to the event loop before socket writes resume.
+
+## Backpressure
+
+The application queue is bounded by the server connection capacity. Submitting to a full or stopping pool is rejected instead of allocating an unbounded linked list or growing queue.
+
+Runtime metrics expose queue depth, active workers, rejected jobs, completed jobs, and total queue wait time.
+
+Other request limits remain enforced independently, including maximum clients, headers, body, and upload size.
+
+## Ownership and thread safety
+
+The event loop owns live connection objects except while a connection is executing inside the worker pool. The worker returns ownership through the completion queue.
+
+Routes, middleware, and server configuration are configured before startup and treated as immutable while the server is running.
+
+Metrics and completion queues use explicit synchronization. Runtime stop state is atomic.
+
+Public request-owned memory is valid until request cleanup unless it is detached. Public response-owned memory is released by `cHTTPX_ResponseCleanup()`.
+
+## Public API and ABI policy
+
+Backward-compatible additions are allowed in MINOR releases. Breaking signature, semantic, or public layout changes require a MAJOR release.
+
+Opaque/internal state stays under `src/` and must not be promoted to the public header without a compatibility reason.
+
+Canonical public result names use `cHTTPX_*`. Legacy `CHTTPX_*` result macros are compatibility aliases and may be disabled with `CHTTPX_DISABLE_LEGACY_ERROR_NAMES`.
+
+## Error model
+
+`chttpx_error_t` APIs return `cHTTPX_OK` or a negative `cHTTPX_ERR_*` value.
+
+Pointer APIs use `NULL` for failure. APIs that intentionally use another convention must document it.
+
+The library should log an error once at the layer that has actionable context and still return the error to the application when the application needs to decide what to do.
+
+## Security boundary
+
+The HTTP parser and framing code process attacker-controlled bytes. Changes must be tested with malformed, fragmented, oversized, conflicting, truncated, and ambiguous input.
+
+The fuzz smoke target continuously exercises header parsing. Parser regression tests remain the source for exact expected behavior around framing rules.
+
+## CI merge gates
+
+The expected order is:
+
+```text
+format/static analysis
+ -> build matrix
+ -> unit/integration tests
+ -> ASan/UBSan/TSan
+ -> parser fuzz smoke
+```
+
+A sanitizer or fuzz failure blocks merge even when normal unit tests pass.
+
+## Documentation source of truth
+
+Module documentation lives under `docs/`. The website should consume or link to that content rather than maintaining separate hand-written copies of the same API description.
+
+## Benchmark protocol
+
+Performance comparisons must record:
+
+- CPU and RAM;
+- operating system;
+- compiler and flags;
+- connection concurrency;
+- request/response payload;
+- HTTP/TLS mode;
+- keep-alive mode;
+- requests per second;
+- p50/p95/p99 latency;
+- CPU utilization;
+- memory per connection.
+
+Benchmarks are tracked separately from correctness CI because noisy timing variance must not block unrelated pull requests.
diff --git a/docs/metrics/README.md b/docs/metrics/README.md
index c9e4403..fa437a0 100644
--- a/docs/metrics/README.md
+++ b/docs/metrics/README.md
@@ -23,7 +23,7 @@ The configuration must be set before the server is created.
```c
chttpx_metrics_t metrics;
-if (cHTTPX_ServerMetrics(server, &metrics) == CHTTPX_OK) {
+if (cHTTPX_ServerMetrics(server, &metrics) == cHTTPX_OK) {
printf("requests: %llu\n",
(unsigned long long)metrics.requests_total);
printf("in flight: %llu\n",
@@ -42,6 +42,10 @@ The snapshot includes:
- parser, timeout and rate-limit failure counters;
- request-duration count, sum and cumulative histogram buckets.
+Worker-runtime metrics are available through `cHTTPX_ServerRuntimeMetrics()`. They include the bounded application queue depth, active workers, rejected jobs, completed jobs, and total queue-wait time. The worker pool is fixed at 32 threads and is intentionally not a public configuration knob.
+
+The Prometheus exporter also publishes these values as `libchttpx_worker_queue_depth`, `libchttpx_workers_active`, `libchttpx_worker_jobs_rejected_total`, `libchttpx_worker_jobs_completed_total`, and `libchttpx_worker_queue_wait_seconds_total`.
+
The duration buckets are:
```text
@@ -65,7 +69,7 @@ chttpx_router_t router = cHTTPX_RoutePathPrefix(server, "");
cHTTPX_Get(&router, "/health", health_handler);
-if (cHTTPX_MetricsRoute(&router, "/metrics") != CHTTPX_OK) {
+if (cHTTPX_MetricsRoute(&router, "/metrics") != cHTTPX_OK) {
/* metrics are disabled or the route could not be registered */
}
```
diff --git a/docs/metrics/README_RU.md b/docs/metrics/README_RU.md
index 8f55fae..97b7131 100644
--- a/docs/metrics/README_RU.md
+++ b/docs/metrics/README_RU.md
@@ -23,7 +23,7 @@ chttpx_serv_t *server = cHTTPX_AppServer(&app, "api", &config);
```c
chttpx_metrics_t metrics;
-if (cHTTPX_ServerMetrics(server, &metrics) == CHTTPX_OK) {
+if (cHTTPX_ServerMetrics(server, &metrics) == cHTTPX_OK) {
printf("requests: %llu\n",
(unsigned long long)metrics.requests_total);
printf("in flight: %llu\n",
@@ -65,7 +65,7 @@ chttpx_router_t router = cHTTPX_RoutePathPrefix(server, "");
cHTTPX_Get(&router, "/health", health_handler);
-if (cHTTPX_MetricsRoute(&router, "/metrics") != CHTTPX_OK) {
+if (cHTTPX_MetricsRoute(&router, "/metrics") != cHTTPX_OK) {
/* metrics выключены или route не удалось зарегистрировать */
}
```
diff --git a/docs/responses/README.md b/docs/responses/README.md
index 5056843..54665d1 100644
--- a/docs/responses/README.md
+++ b/docs/responses/README.md
@@ -77,8 +77,8 @@ Use `cHTTPX_Status*` and `cHTTPX_CTYPE_*` constants from `http.h`. `cHTTPX_Statu
## Ownership
-- `CHTTPX_BODY_OWNED` → response cleanup frees the body;
-- `CHTTPX_BODY_BORROWED` → library does not free the body.
+- `cHTTPX_BODY_OWNED` → response cleanup frees the body;
+- `cHTTPX_BODY_BORROWED` → library does not free the body.
Response helpers that allocate content create owned responses. Normal handler code should not manually free helper-produced bodies; the server cleans them after send.
diff --git a/docs/responses/README_RU.md b/docs/responses/README_RU.md
index 6b1de00..4a5feab 100644
--- a/docs/responses/README_RU.md
+++ b/docs/responses/README_RU.md
@@ -79,8 +79,8 @@ cHTTPX_HeaderAdd(
## Ownership
-- `CHTTPX_BODY_OWNED` — cleanup освобождает body;
-- `CHTTPX_BODY_BORROWED` — библиотека body не освобождает.
+- `cHTTPX_BODY_OWNED` — cleanup освобождает body;
+- `cHTTPX_BODY_BORROWED` — библиотека body не освобождает.
Response helpers, которые выделяют память, создают owned response. В обычном handler не освобождайте их body вручную.
diff --git a/docs/server/README.md b/docs/server/README.md
index 9514311..78c99a3 100644
--- a/docs/server/README.md
+++ b/docs/server/README.md
@@ -17,7 +17,7 @@ Current defaults:
| Field | Default |
| --- | ---: |
| `port` | 8080 |
-| `network_mode` | `CHTTPX_NETWORK_DUAL` |
+| `network_mode` | `cHTTPX_NETWORK_DUAL` |
| `max_clients` | 255 |
| `read_timeout_sec` | 30 |
| `write_timeout_sec` | 30 |
@@ -28,7 +28,7 @@ Current defaults:
| `request_id_enabled` | true |
| `metrics_enabled` | false |
| `default_language` | `"en"` |
-| `log_level` | `CHTTPX_LOG_INFO` |
+| `log_level` | `cHTTPX_LOG_INFO` |
## Network mode
@@ -38,16 +38,16 @@ Servers use dual-stack networking by default. A single IPv6 listener bound to `:
chttpx_config_t config = cHTTPX_DefaultConfig();
/* Default: accept IPv4 and IPv6. */
-config.network_mode = CHTTPX_NETWORK_DUAL;
+config.network_mode = cHTTPX_NETWORK_DUAL;
/* IPv4 only. */
-// config.network_mode = CHTTPX_NETWORK_IPV4;
+// config.network_mode = cHTTPX_NETWORK_IPV4;
/* IPv6 only. */
-// config.network_mode = CHTTPX_NETWORK_IPV6;
+// config.network_mode = cHTTPX_NETWORK_IPV6;
```
-With `CHTTPX_NETWORK_DUAL`, both `http://127.0.0.1:8080` and `http://[::1]:8080` reach the same server. IPv4-mapped peer addresses are normalized before they are exposed through `req->client_ip`, so an IPv4 client is reported as `127.0.0.1` rather than `::ffff:127.0.0.1`.
+With `cHTTPX_NETWORK_DUAL`, both `http://127.0.0.1:8080` and `http://[::1]:8080` reach the same server. IPv4-mapped peer addresses are normalized before they are exposed through `req->client_ip`, so an IPv4 client is reported as `127.0.0.1` rather than `::ffff:127.0.0.1`.
## Custom limits
@@ -87,26 +87,26 @@ See [Metrics and Prometheus](../metrics/README.md) for snapshots and the `/metri
| Code | Meaning |
| --- | --- |
-| `CHTTPX_OK` | success |
-| `CHTTPX_ERR_MEMORY` | allocation failure |
-| `CHTTPX_ERR_SOCKET` | socket setup failure |
-| `CHTTPX_ERR_BIND` | bind failed |
-| `CHTTPX_ERR_LISTEN` | listen failed |
-| `CHTTPX_ERR_INVALID_ARGUMENT` | invalid input |
-| `CHTTPX_ERR_LIMIT` | limit exceeded |
-| `CHTTPX_ERR_IO` | I/O failure |
-| `CHTTPX_ERR_NOT_FOUND` | resource/target not found |
-| `CHTTPX_ERR_PROTOCOL` | invalid protocol data |
-| `CHTTPX_ERR_STATE` | invalid lifecycle state |
-| `CHTTPX_ERR_UNAVAILABLE` | remote call unavailable |
-| `CHTTPX_ERR_TIMEOUT` | remote call timed out |
+| `cHTTPX_OK` | success |
+| `cHTTPX_ERR_MEMORY` | allocation failure |
+| `cHTTPX_ERR_SOCKET` | socket setup failure |
+| `cHTTPX_ERR_BIND` | bind failed |
+| `cHTTPX_ERR_LISTEN` | listen failed |
+| `cHTTPX_ERR_INVALID_ARGUMENT` | invalid input |
+| `cHTTPX_ERR_LIMIT` | limit exceeded |
+| `cHTTPX_ERR_IO` | I/O failure |
+| `cHTTPX_ERR_NOT_FOUND` | resource/target not found |
+| `cHTTPX_ERR_PROTOCOL` | invalid protocol data |
+| `cHTTPX_ERR_STATE` | invalid lifecycle state |
+| `cHTTPX_ERR_UNAVAILABLE` | remote call unavailable |
+| `cHTTPX_ERR_TIMEOUT` | remote call timed out |
## Lifecycle
```c
chttpx_app_t app;
-if (cHTTPX_AppInit(&app) != CHTTPX_OK)
+if (cHTTPX_AppInit(&app) != cHTTPX_OK)
return 1;
chttpx_config_t config = cHTTPX_DefaultConfig();
@@ -134,6 +134,14 @@ cHTTPX_AppShutdown(&app);
Trigger shutdown from an appropriate control thread for SIGINT/SIGTERM. Avoid complex work directly in an async-signal handler.
+## Event-driven runtime and worker pool
+
+Sockets are non-blocking and stay on the server event loop instead of occupying worker threads while waiting for network I/O. Linux uses `epoll`.
+
+A connection is submitted to the internal fixed pool of **32 worker threads** only after the complete HTTP request has been received. Workers execute middleware, routing and the application handler; they do not call `recv()` or `send()`. The serialized response is returned to the event loop and written when the socket is ready.
+
+The worker count is intentionally not part of `chttpx_config_t` and cannot be changed by application code. `max_clients` limits accepted/in-flight connections, not the number of OS threads. Large multipart/chunked upload bodies remain disk-backed while they are received.
+
## Thread-safety model
- client count is updated atomically;
@@ -143,4 +151,4 @@ Trigger shutdown from an appropriate control thread for SIGINT/SIGTERM. Avoid co
## HTTP model
-Current server behavior is HTTP/1.1 with one request per connection and explicit `Connection: close`. Fixed `Content-Length` and chunked request bodies are supported.
+Current server behavior is HTTP/2. Cleartext servers use h2c prior knowledge; TLS servers negotiate `h2` with ALPN. Multiple HTTP/2 streams may share one connection, while the public App/router/handler API remains unchanged.
diff --git a/docs/server/README_RU.md b/docs/server/README_RU.md
index ce6c7ac..61ed64d 100644
--- a/docs/server/README_RU.md
+++ b/docs/server/README_RU.md
@@ -17,7 +17,7 @@ chttpx_serv_t* server =
| Поле | Default |
| --- | ---: |
| `port` | 8080 |
-| `network_mode` | `CHTTPX_NETWORK_DUAL` |
+| `network_mode` | `cHTTPX_NETWORK_DUAL` |
| `max_clients` | 255 |
| `read_timeout_sec` | 30 |
| `write_timeout_sec` | 30 |
@@ -28,7 +28,7 @@ chttpx_serv_t* server =
| `request_id_enabled` | true |
| `metrics_enabled` | false |
| `default_language` | `"en"` |
-| `log_level` | `CHTTPX_LOG_INFO` |
+| `log_level` | `cHTTPX_LOG_INFO` |
## Сетевой режим
@@ -38,16 +38,16 @@ chttpx_serv_t* server =
chttpx_config_t config = cHTTPX_DefaultConfig();
/* Default: IPv4 + IPv6. */
-config.network_mode = CHTTPX_NETWORK_DUAL;
+config.network_mode = cHTTPX_NETWORK_DUAL;
/* Только IPv4. */
-// config.network_mode = CHTTPX_NETWORK_IPV4;
+// config.network_mode = cHTTPX_NETWORK_IPV4;
/* Только IPv6. */
-// config.network_mode = CHTTPX_NETWORK_IPV6;
+// config.network_mode = cHTTPX_NETWORK_IPV6;
```
-При `CHTTPX_NETWORK_DUAL` один server доступен и через `http://127.0.0.1:8080`, и через `http://[::1]:8080`. IPv4-mapped адреса нормализуются перед записью в `req->client_ip`, поэтому IPv4 client будет иметь адрес `127.0.0.1`, а не `::ffff:127.0.0.1`.
+При `cHTTPX_NETWORK_DUAL` один server доступен и через `http://127.0.0.1:8080`, и через `http://[::1]:8080`. IPv4-mapped адреса нормализуются перед записью в `req->client_ip`, поэтому IPv4 client будет иметь адрес `127.0.0.1`, а не `::ffff:127.0.0.1`.
## Собственные лимиты
@@ -82,19 +82,19 @@ Snapshot API и Prometheus endpoint описаны в [Metrics и Prometheus](..
| Код | Значение |
| --- | --- |
-| `CHTTPX_OK` | успех |
-| `CHTTPX_ERR_MEMORY` | allocation failure |
-| `CHTTPX_ERR_SOCKET` | socket error |
-| `CHTTPX_ERR_BIND` | bind failed |
-| `CHTTPX_ERR_LISTEN` | listen failed |
-| `CHTTPX_ERR_INVALID_ARGUMENT` | неверные аргументы |
-| `CHTTPX_ERR_LIMIT` | превышен limit |
-| `CHTTPX_ERR_IO` | I/O error |
-| `CHTTPX_ERR_NOT_FOUND` | target/resource не найден |
-| `CHTTPX_ERR_PROTOCOL` | protocol error |
-| `CHTTPX_ERR_STATE` | неверное состояние lifecycle |
-| `CHTTPX_ERR_UNAVAILABLE` | remote server недоступен |
-| `CHTTPX_ERR_TIMEOUT` | remote Call timeout |
+| `cHTTPX_OK` | успех |
+| `cHTTPX_ERR_MEMORY` | allocation failure |
+| `cHTTPX_ERR_SOCKET` | socket error |
+| `cHTTPX_ERR_BIND` | bind failed |
+| `cHTTPX_ERR_LISTEN` | listen failed |
+| `cHTTPX_ERR_INVALID_ARGUMENT` | неверные аргументы |
+| `cHTTPX_ERR_LIMIT` | превышен limit |
+| `cHTTPX_ERR_IO` | I/O error |
+| `cHTTPX_ERR_NOT_FOUND` | target/resource не найден |
+| `cHTTPX_ERR_PROTOCOL` | protocol error |
+| `cHTTPX_ERR_STATE` | неверное состояние lifecycle |
+| `cHTTPX_ERR_UNAVAILABLE` | remote server недоступен |
+| `cHTTPX_ERR_TIMEOUT` | remote Call timeout |
`cHTTPX_AppServer()` возвращает pointer или `NULL`. Библиотека не вызывает `exit()` при обычной ошибке инициализации.
@@ -103,7 +103,7 @@ Snapshot API и Prometheus endpoint описаны в [Metrics и Prometheus](..
```c
chttpx_app_t app;
-if (cHTTPX_AppInit(&app) != CHTTPX_OK)
+if (cHTTPX_AppInit(&app) != cHTTPX_OK)
return 1;
chttpx_config_t config = cHTTPX_DefaultConfig();
@@ -123,6 +123,14 @@ cHTTPX_AppShutdown(&app);
`cHTTPX_AppShutdown()` прекращает accept, закрывает listeners, ждёт активные requests, освобождает routes, CORS, middleware state, server objects и remote registrations.
+## Event-driven runtime и worker pool
+
+Sockets работают в non-blocking режиме и остаются на server event loop, поэтому worker thread больше не занят ожиданием сетевого I/O. На Linux используется `epoll`.
+
+Connection попадает во внутренний фиксированный пул из **32 worker threads** только после того, как HTTP request полностью принят. Worker выполняет middleware, routing и application handler и не вызывает `recv()` или `send()`. Готовый сериализованный response возвращается в event loop и отправляется только когда socket готов к записи.
+
+Количество workers намеренно не добавлено в `chttpx_config_t` и не может изменяться кодом приложения. `max_clients` ограничивает число accepted/in-flight connections, а не количество OS threads. Большие multipart/chunked uploads во время приёма остаются disk-backed.
+
## Thread safety
Routes/middleware настраивайте до `AppStart/AppRun`. Во время работы они должны рассматриваться как read-only.
@@ -131,4 +139,4 @@ Routes/middleware настраивайте до `AppStart/AppRun`. Во врем
## HTTP model
-Текущая реализация работает по HTTP/1.1 с одним request на connection и явным `Connection: close`. Поддерживаются `Content-Length` и chunked request bodies.
+Текущая реализация работает по HTTP/2. Для соединений без TLS используется h2c prior knowledge, а TLS-серверы согласовывают `h2` через ALPN. Несколько HTTP/2 stream могут использовать одно соединение, при этом публичный App/router/handler API не меняется.
diff --git a/docs/site/README.md b/docs/site/README.md
index c8ca670..c3f364a 100644
--- a/docs/site/README.md
+++ b/docs/site/README.md
@@ -2,6 +2,9 @@
Static project website for GitHub Pages.
+The documentation layout follows a Mongoose-style docs UI: sticky header,
+collapsible left sidebar with search, chevron breadcrumbs, and dark code blocks.
+
## Files
- `index.html` — landing page
@@ -12,9 +15,8 @@ Static project website for GitHub Pages.
- `blog.html` — project updates and release links
- `support.html` — community, commercial and sponsorship support
- `about.html` — project information
-- `styles.css` — shared styles
-- `app.js` — mobile navigation, copy buttons, docs filter and GitHub repository stats
-- `styles.css` — shared styles and DM Sans typography
+- `styles.css` — shared documentation layout (sidebar, breadcrumbs, dark code)
+- `app.js` — navigation, sidebar, copy buttons, syntax highlighting and GitHub stats
The site has no framework. GitHub Pages has a small build step that recreates `docs/site/content` from the canonical module READMEs under `docs/`, so website documentation cannot drift from repository documentation.
diff --git a/docs/site/about.html b/docs/site/about.html
index 69e832a..d42997f 100644
--- a/docs/site/about.html
+++ b/docs/site/about.html
@@ -10,82 +10,67 @@
-
-
-
-
About
-
A small C library focused on practical HTTP server work
-
- libchttpx is maintained by netcorelink and developed in public on GitHub.
-
-
-
+
+
+
+
+
+ Home
+ About
+
-
-
-
-
Project goal
-
- Keep the API direct and C-like while providing the pieces commonly needed by real backend services:
- routing, middleware, request parsing, JSON, uploads, CORS, cookies, logging, rate limiting and graceful shutdown.
-
-
-
+
About
+
+ libchttpx is a small C library focused on practical HTTP server work.
+ It is maintained by netcorelink and developed in public on GitHub.
+
+
Project goal
+
+ Keep the API direct and C-like while providing the pieces commonly needed
+ by real backend services: routing, middleware, request parsing, JSON,
+ uploads, CORS, cookies, logging, rate limiting and graceful shutdown.
+
Written primarily in C
- Cross-platform Linux and Windows support
+ Linux-only runtime and build
Public issue tracker and source history
BSD 3-Clause license
Examples included in the repository
Documentation versioned with the code
-
-
-
-
Maintained in public
-
- Bugs, changes and releases are tracked on GitHub. This website is intentionally a simple front door to the project,
- while the repository remains the source of truth.
+
+ Bugs, changes and releases are tracked on GitHub. This website is a
+ front door to the project; the repository remains the source of truth.
-
-
Source Read the implementation and follow development.
GitHub ↗
-
Issues Report a bug or propose a feature.
Issues ↗
-
Releases Download stable published versions.
Releases ↗
-
-
-
-
-
-
+
+ Source ·
+ Issues ·
+ Releases ·
+ License
+
+
+
+