Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 46 additions & 44 deletions Makefile

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ A C++17 network server and gateway built on the Reactor pattern. It uses non-blo
- **TLS/SSL** — OpenSSL 3.x integration for downstream server TLS and upstream client TLS
- **Upstream Proxy** — per-service connection pools with TLS, streaming response relay, retry policy, trailer handling, and header rewriting
- **HTTP/2 Upstream** — per-upstream opt-in multiplexed H2 client (donated-lease pattern), ALPN-negotiated `auto / always / never` dispatch, two-deadline send-stall + response-timeout model, transport-drain-driven sink dispatch, GOAWAY/PING liveness, live-reloadable session settings
- **gRPC Proxying** — inbound classifier (HTTP/2 + `application/grpc` content-type, with per-route `proxy.protocol: auto/grpc/rest` override), `grpc-timeout` quad-state parser + dedicated `ArmGrpcDeadline` enforcement, Trailers-Only synthesis for every middleware-reject / proxy-failure / deadline path (`MakeGrpcErrorResponse` + HTTP→gRPC status mapping), `te: trailers` outbound force-fold, distinct `RESULT_GRPC_DEADLINE_EXCEEDED` for circuit-breaker classification, happy-path peer-trailer `grpc-status` snapshot for observability
- **gRPC Proxying** — inbound classifier (HTTP/2 + `application/grpc` content-type, with per-route `proxy.protocol: auto/grpc/rest` override), `grpc-timeout` quad-state parser + dedicated `ArmGrpcDeadline` enforcement, Trailers-Only synthesis for every middleware-reject / proxy-failure / deadline path, `te: trailers` outbound force-fold, distinct `RESULT_GRPC_DEADLINE_EXCEEDED` for circuit-breaker classification, trailer-status retry on `grpc-status: UNAVAILABLE` with gRFC A6 pushback support, OTel SERVER/CLIENT span split per gRPC semconv with `rpc.server.call.duration` + `rpc.client.call.duration` histograms, and a per-route gRPC-Web bridge (`proxy.grpc_web.enabled`) that translates `application/grpc-web[-text]` from HTTP/1.1 + HTTP/2 carriers into canonical gRPC
- **Rate Limiting** — per-client / per-route token-bucket middleware with LRU eviction, `RateLimit-*` headers, dry-run mode, hot reload
- **Circuit Breaking** — per-upstream state machines, retry budgets, dry-run mode, wait-queue drain, hot-reloadable breaker tuning
- **OAuth 2.0 Token Validation** — JWT validation with JWKS/OIDC discovery, multi-issuer policies, outbound identity headers, and RFC 7662 introspection mode
Expand Down
4 changes: 2 additions & 2 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Running Tests

```bash
make test # Build and run all suites (1694 tests across 44+ suites at HEAD)
make test # Build and run all suites (1901 tests across 47+ suites at HEAD)
./test_runner # Run all tests directly (after building)
./test_runner help # Print every supported flag

Expand Down Expand Up @@ -95,7 +95,7 @@ make test_auth_race
make test_auth_observability
```

At current head, `./test_runner` reports **1694 / 1694 passing** (100 %).
At current head, `./test_runner` reports **1901 / 1901 passing** (100 %).

## Test Suites

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion test/grpc_obs_test.h → test/grpc/grpc_obs_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
#include "observability/sampler.h"
#include "observability/trace_id.h"

#include "h2_trailer_test.h" // TrailerAwareHttp2Client + FindTrailer
#include "http2/h2_trailer_test.h" // TrailerAwareHttp2Client + FindTrailer

#include <atomic>
#include <chrono>
Expand Down
2 changes: 1 addition & 1 deletion test/grpc_proxy_test.h → test/grpc/grpc_proxy_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
#include "observability/sampler.h"
#include "observability/trace_id.h" // RandomSource

#include "h2_trailer_test.h" // reuse TrailerAwareHttp2Client + FindTrailer
#include "http2/h2_trailer_test.h" // reuse TrailerAwareHttp2Client + FindTrailer
#include "grpc/grpc_web_bridge.h" // BuildTrailerFrame for GW1 expected-bytes
#include "base64.h" // UTIL_NAMESPACE::EncodeNoNewline for GW2
#include "http_test_client.h" // SendHttpRequest, HasStatus, ExtractBody
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
#include "upstream/grpc_web_inbound_body_stream.h"
#include "base64.h"

#include "h2_trailer_test.h" // TrailerAwareHttp2Client, FindTrailer
#include "http2/h2_trailer_test.h" // TrailerAwareHttp2Client, FindTrailer

#include <atomic>
#include <chrono>
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#include <unistd.h>

// Pull in the H2 test client (declared in http2_test.h).
#include "http2_test.h"
#include "http2/http2_test.h"

namespace RouterAsyncMiddlewareTests {

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#include "observability/trace_id.h"
#include "observability/tracer.h"
#include "observability/tracer_provider.h"
#include "observability_test_helpers.h"
#include "observability/observability_test_helpers.h"

#include <cstdint>
#include <string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
#include "test_framework.h"
#include "observability/observability_manager.h"
#include "observability/observability_snapshot.h"
#include "observability_test_helpers.h"
#include "proxy_transaction_internal_test.h"
#include "observability/observability_test_helpers.h"
#include "upstream/proxy_transaction_internal_test.h"

#include <atomic>
#include <chrono>
Expand Down Expand Up @@ -53,7 +53,7 @@ class FakeTxLink final : public UpstreamTransactionLink {

// ---- ProxyTransaction implements the interface correctly ----

void TestProxyTxIsLink() {
inline void TestProxyTxIsLink() {
try {
HttpRequest req;
auto tx = ProxyTransactionInternalTests::MakeInternalProxyTransaction(req);
Expand All @@ -75,7 +75,7 @@ void TestProxyTxIsLink() {

// ---- Linked snapshot kill flow: KillOutstandingSnapshots fires the kill ----

void TestKillFlipsLinkedTransaction() {
inline void TestKillFlipsLinkedTransaction() {
try {
auto m = MakeManager();
auto snap = std::make_shared<ObservabilitySnapshot>();
Expand Down Expand Up @@ -108,7 +108,7 @@ void TestKillFlipsLinkedTransaction() {

// ---- Empty link slot — kill loop must not crash on weak.lock() == null ----

void TestKillTolerantOfMissingLink() {
inline void TestKillTolerantOfMissingLink() {
try {
auto m = MakeManager();
auto snap = std::make_shared<ObservabilitySnapshot>();
Expand All @@ -135,7 +135,7 @@ void TestKillTolerantOfMissingLink() {
// finalized state under link_mtx and immediately mark the link
// killed-for-shutdown so the caller's terminal gates short-circuit.

void TestAttachAfterKillFlipsLinkImmediately() {
inline void TestAttachAfterKillFlipsLinkImmediately() {
try {
auto m = MakeManager();
auto snap = std::make_shared<ObservabilitySnapshot>();
Expand Down Expand Up @@ -192,7 +192,7 @@ void TestAttachAfterKillFlipsLinkImmediately() {
// false, and leave the link's kill flag untouched. The kill loop
// (or any future finalizer) is still in charge of marking the link.

void TestAttachBeforeKillIsBenign() {
inline void TestAttachBeforeKillIsBenign() {
try {
auto m = MakeManager();
auto snap = std::make_shared<ObservabilitySnapshot>();
Expand Down Expand Up @@ -233,7 +233,7 @@ void TestAttachBeforeKillIsBenign() {
// reach `serialized_request_ = HttpRequestSerializer::Serialize(...)`
// and produce a non-empty wire image — the assertion catches that.

void TestStartShortCircuitsOnFinalizedSnapshot() {
inline void TestStartShortCircuitsOnFinalizedSnapshot() {
try {
auto m = MakeManager();
auto snap = std::make_shared<ObservabilitySnapshot>();
Expand Down Expand Up @@ -266,7 +266,7 @@ void TestStartShortCircuitsOnFinalizedSnapshot() {

// ---- AttachObservabilitySnapshot stores the snapshot for the link site ----

void TestAttachSnapshotStoresHandle() {
inline void TestAttachSnapshotStoresHandle() {
try {
HttpRequest req;
auto tx = ProxyTransactionInternalTests::MakeInternalProxyTransaction(req);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "test_framework.h"
#include "observability/observability_manager.h"
#include "observability/observability_snapshot.h"
#include "observability_test_helpers.h"
#include "observability/observability_test_helpers.h"

#include <chrono>
#include <memory>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include "observability/observability_manager.h"
#include "observability/observability_snapshot.h"
#include "observability/resource.h"
#include "observability_test_helpers.h"
#include "observability/observability_test_helpers.h"

#include <atomic>
#include <chrono>
Expand Down
File renamed without changes.
192 changes: 118 additions & 74 deletions test/run_test.cc
Original file line number Diff line number Diff line change
@@ -1,81 +1,125 @@
#include "proxy_transaction_internal_test.h"
#include "http_internal_test.h"
#include "http2_internal_test.h"
#include "stress_test.h"
// Internal tests use `#define private public` to access private members of
// production classes. They MUST be included before any other header pulls in
// those production classes with default access (the production headers'
// #pragma once guard would otherwise prevent a second include with the macro
// in effect). Within this group, proxy_transaction_internal_test.h goes FIRST
// because its `#define private public` block also covers ConnectionHandler /
// SocketHandler / UpstreamConnection — superset of the other internal tests'
// reach.
#include "upstream/proxy_transaction_internal_test.h"
#include "http/http_internal_test.h"
#include "http2/http2_internal_test.h"
#include "observability/observability_link_kill_test.h"

// Test suite headers — grouped by feature subfolder (mirroring include/),
// alphabetical within each group. Root-level cross-cutting tests + the shared
// test_framework header follow at the end. Subfolder names are lowercase to
// match include/'s convention; namespace identifiers (e.g. HTTP_NAMESPACE)
// remain UPPER_SNAKE_CASE per CODE_CONVENTIONS.md but folder paths do not.

// auth/
#include "auth/auth_failure_mode_test.h"
#include "auth/auth_foundation_test.h"
#include "auth/auth_integration_test.h"
#include "auth/auth_introspection_integration_test.h"
#include "auth/auth_manager_test.h"
#include "auth/auth_multi_issuer_test.h"
#include "auth/auth_observability_test.h"
#include "auth/auth_race_test.h"
#include "auth/auth_reload_test.h"
#include "auth/auth_websocket_upgrade_test.h"
#include "auth/header_rewriter_auth_test.h"
#include "auth/introspection_cache_test.h"
#include "auth/introspection_client_test.h"
#include "auth/jwks_cache_test.h"
#include "auth/jwt_verifier_test.h"
#include "auth/oidc_discovery_test.h"

// circuit_breaker/
#include "circuit_breaker/circuit_breaker_components_test.h"
#include "circuit_breaker/circuit_breaker_integration_test.h"
#include "circuit_breaker/circuit_breaker_observability_test.h"
#include "circuit_breaker/circuit_breaker_reload_test.h"
#include "circuit_breaker/circuit_breaker_retry_budget_test.h"
#include "circuit_breaker/circuit_breaker_test.h"
#include "circuit_breaker/circuit_breaker_wait_queue_drain_test.h"

// cli/
#include "cli/cli_test.h"

// config/
#include "config/config_test.h"

// grpc/
#include "grpc/grpc_obs_test.h"
#include "grpc/grpc_proxy_test.h"
#include "grpc/grpc_test.h"
#include "grpc/grpc_web_edge_test.h"
#include "grpc/grpc_web_test.h"

// http/ (http_internal_test.h included above in the internal-tests group)
#include "http/http_test.h"
#include "http/route_test.h"
#include "http/router_async_middleware_test.h"
#include "http/streaming_request_test.h"
#include "http/timeout_test.h"

// http2/ (http2_internal_test.h included above in the internal-tests group)
#include "http2/h2_trailer_test.h"
#include "http2/http2_test.h"

// net/
#include "net/dns_resolver_test.h"
#include "net/dual_stack_test.h"

// observability/
#include "observability/observability_auth_trace_test.h"
#include "observability/observability_catalog_test.h"
#include "observability/observability_config_test.h"
#include "observability/observability_connection_metrics_test.h"
#include "observability/observability_e2e_test.h"
#include "observability/observability_export_pipeline_test.h"
#include "observability/observability_foundation_test.h"
#include "observability/observability_issue_inject_test.h"
#include "observability/observability_jaeger_propagator_test.h"
#include "observability/observability_kill_marshal_test.h"
// observability_link_kill_test.h included above in the internal-tests group
#include "observability/observability_manager_test.h"
#include "observability/observability_metrics_test.h"
#include "observability/observability_middleware_metrics_test.h"
#include "observability/observability_pool_gauges_test.h"
#include "observability/observability_prometheus_test.h"
#include "observability/observability_propagator_test.h"
#include "observability/observability_proxy_client_test.h"
#include "observability/observability_self_handler_test.h"
#include "observability/observability_self_metrics_test.h"
#include "observability/observability_shutdown_test.h"
#include "observability/observability_stress_test.h"
#include "observability/observability_tracer_test.h"
#include "observability/observability_ws_messages_test.h"

// rate_limit/
#include "rate_limit/rate_limit_test.h"

// tls/
#include "tls/tls_test.h"

// upstream/ (proxy_transaction_internal_test.h included above in the internal-tests group)
#include "upstream/h2_upstream_test.h"
#include "upstream/proxy_test.h"
#include "upstream/upstream_pool_test.h"

// ws/
#include "ws/websocket_test.h"

// test/ (root — cross-cutting infrastructure + shared framework)
#include "basic_test.h"
#include "race_condition_test.h"
#include "timeout_test.h"
#include "config_test.h"
#include "http_test.h"
#include "websocket_test.h"
#include "tls_test.h"
#include "cli_test.h"
#include "http2_test.h"
#include "route_test.h"
#include "kqueue_test.h"
#include "upstream_pool_test.h"
#include "proxy_test.h"
#include "rate_limit_test.h"
#include "circuit_breaker_test.h"
#include "circuit_breaker_components_test.h"
#include "circuit_breaker_integration_test.h"
#include "circuit_breaker_retry_budget_test.h"
#include "circuit_breaker_wait_queue_drain_test.h"
#include "circuit_breaker_observability_test.h"
#include "circuit_breaker_reload_test.h"
#include "auth_foundation_test.h"
#include "jwt_verifier_test.h"
#include "jwks_cache_test.h"
#include "oidc_discovery_test.h"
#include "header_rewriter_auth_test.h"
#include "auth_manager_test.h"
#include "auth_integration_test.h"
#include "auth_failure_mode_test.h"
#include "auth_reload_test.h"
#include "auth_multi_issuer_test.h"
#include "auth_websocket_upgrade_test.h"
#include "auth_race_test.h"
#include "dns_resolver_test.h"
#include "dual_stack_test.h"
#include "h2_upstream_test.h"
#include "router_async_middleware_test.h"
#include "introspection_cache_test.h"
#include "race_condition_test.h"
#include "sharded_lru_cache_test.h"
#include "introspection_client_test.h"
#include "auth_introspection_integration_test.h"
#include "auth_observability_test.h"
#include "observability_foundation_test.h"
#include "observability_tracer_test.h"
#include "observability_metrics_test.h"
#include "observability_manager_test.h"
#include "observability_propagator_test.h"
#include "observability_jaeger_propagator_test.h"
#include "observability_export_pipeline_test.h"
#include "observability_prometheus_test.h"
#include "observability_config_test.h"
#include "observability_shutdown_test.h"
#include "observability_link_kill_test.h"
#include "observability_issue_inject_test.h"
#include "observability_stress_test.h"
#include "observability_e2e_test.h"
#include "observability_self_handler_test.h"
#include "observability_proxy_client_test.h"
#include "observability_auth_trace_test.h"
#include "observability_catalog_test.h"
#include "observability_middleware_metrics_test.h"
#include "observability_kill_marshal_test.h"
#include "observability_ws_messages_test.h"
#include "observability_self_metrics_test.h"
#include "observability_connection_metrics_test.h"
#include "observability_pool_gauges_test.h"
#include "streaming_request_test.h"
#include "h2_trailer_test.h"
#include "grpc_test.h"
#include "grpc_proxy_test.h"
#include "grpc_obs_test.h"
#include "grpc_web_test.h"
#include "grpc_web_edge_test.h"
#include "stress_test.h"
#include "test_framework.h"

#include <algorithm>
#include <sys/resource.h>

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5622,7 +5622,7 @@ void TestN9pH2ResponseTimeoutClosureHonorsShutdownKill() {
// the lighter regression-prevention check.
bool pass = false;
try {
std::ifstream in("server/proxy_transaction.cc");
std::ifstream in("server/upstream/proxy_transaction.cc");
std::string src((std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>());
// Locate the H2 response-timeout closure (uniquely identified
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading