structured-proxy 5.0.0

Universal gRPC→REST transcoding proxy — config-driven, works with any gRPC service
Documentation
[package]
name = "structured-proxy"
description = "Universal gRPC→REST transcoding proxy — config-driven, works with any gRPC service"
version = "5.0.0"
edition = "2021"
authors = ["Dmitry Prudnikov <mail@polaz.com>"]
license = "Apache-2.0"
repository = "https://github.com/structured-world/structured-proxy"
homepage = "https://github.com/structured-world/structured-proxy"
documentation = "https://docs.rs/structured-proxy"
readme = "README.md"
keywords = ["grpc", "rest", "proxy", "transcoding", "protobuf"]
categories = ["network-programming", "web-programming::http-server"]

# docs.rs builds with the `redis` feature on top of the defaults so the
# Redis-backed rate-limit store (and its intra-doc links) are documented.
[package.metadata.docs.rs]
features = ["redis"]

[lib]
name = "structured_proxy"
path = "src/lib.rs"

# The standalone proxy: `cargo install structured-proxy --features cli`. Behind
# the `cli` feature, so a crate that depends on the library compiles none of
# its dependencies.
[[bin]]
name = "structured-proxy"
path = "src/main.rs"
required-features = ["cli"]
# Its crate name is the library's; the library's rustdoc is the one to keep.
doc = false

[dependencies]
# HTTP framework
axum = { version = "0.8", features = ["macros"] }
tower = "0.5"
tower-http = { version = "0.7", features = ["cors", "trace"] }
# Foundational HTTP types, used directly by the framework-agnostic embedding
# hooks (src/hooks.rs) so an embedder never names `axum`. Already in the tree
# transitively via both axum and tonic; pinned here to make the public API deps
# explicit.
http = "1"
bytes = "1"

# gRPC client (to upstream service)
tonic = "0.14"
tonic-health = "0.14"
# Canonical google.rpc.Status / error_details descriptors (FILE_DESCRIPTOR_SET)
# and the Status message used to decode `grpc-status-details-bin`, so REST error
# bodies can render typed details even when the product descriptors do not
# import google/rpc/*.
tonic-types = "0.14"
prost = "0.14"

# Async runtime
tokio = { version = "1", features = ["full"] }

# Serialization
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.9"

# Streaming (server-streaming RPC handler)
futures = "0.3"

# Protobuf reflection (REST↔gRPC transcoding)
prost-reflect = { version = "0.16", features = ["serde"] }

# Logging
tracing = "0.1"

# Error handling
thiserror = "2"
anyhow = "1"

# Metrics
prometheus = "0.14"

# Rate limiting (Shield middleware)
dashmap = "6"
async-trait = "0.1"
ipnet = "2"
# Optional shared rate-limit store for multi-instance deployments.
redis = { version = "1.2", features = ["tokio-comp", "connection-manager"], optional = true }

# Auth (JWT validation, route policies).
# Optional: it backs the BUILT-IN verifier only. A consumer that injects its own
# `hooks::TokenVerifier` builds with `default-features = false` and links no JWT
# crypto at all. Crypto backend is selected via this crate's `rust_crypto` /
# `aws_lc_rs` features (see [features] below); `use_pem` is always required for
# PEM key parsing and is therefore enabled unconditionally.
jsonwebtoken = { version = "11", default-features = false, features = [
  "use_pem",
], optional = true }
# Outbound HTTPS (JWKS fetches, the rate-limit service). reqwest 0.13's `rustls`
# feature hardwires the aws-lc-rs provider (C FFI), so `rustls-no-provider`
# builds rustls without one; src/tls.rs hands reqwest a fully preconfigured
# ClientConfig with the Mozilla roots from webpki-roots. Bundling the roots
# keeps the binary self-contained, so it works on musl / scratch / distroless
# images with no system CA bundle. The crypto provider follows the crypto
# backend features below: none is linked without one, and the process-wide
# rustls provider the embedder installs is used instead.
reqwest = { version = "0.13", default-features = false, features = ["rustls-no-provider", "json"] }
rustls = { version = "0.23", default-features = false, features = ["std", "tls12"] }
# The pure-Rust provider of the `rust_crypto` backend. `0.0.2-alpha` still
# names its `rustls-webpki` 0.102 dependency, which carries four CRL /
# name-constraint advisories; the provider only reads algorithm identifier
# constants from it, while certificates are verified by rustls' own (patched)
# webpki. See deny.toml.
rustls-rustcrypto = { version = "0.0.2-alpha", optional = true }
webpki-roots = "1"
globset = "0.4"
base64 = "0.23"
sha2 = "0.11"
url = "2"
getrandom = "0.4"
# Envoy ext_authz `Authorization/Check` messages (External AuthZ).
envoy-types = "0.7"

# The binary's command line and log output (the `cli` feature).
clap = { version = "4", features = ["derive"], optional = true }
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"], optional = true }

[features]
default = ["rust_crypto"]

# The built-in JWT verifier (config-driven keys: PEM file or JWKS endpoint),
# implemented with `jsonwebtoken`. Implied by both crypto backends below, so it
# is never enabled on its own. With neither backend the crate links no JWT
# crypto: `auth.mode: "jwt"` then requires a verifier injected through
# `ProxyServer::with_token_verifier`.
builtin_jwt = ["dep:jsonwebtoken"]

# Crypto backend for the BUILT-IN verifier and the outbound TLS client.
# Additive, like every Cargo feature: with both on, jsonwebtoken cannot infer a
# provider, so the built-in verifier installs `aws_lc_rs` (constant-time,
# advisory-free) for the process, and TLS uses aws-lc too. They no longer
# decide for the whole dependency graph: a consumer that cannot accept this
# crate's choice supplies its own verifier (see `builtin_jwt`) and installs its
# own rustls provider. With neither, the crate links no crypto backend at all.
#
# `rust_crypto` (default): pure-Rust RustCrypto backend. Pulls in `rsa`, which
# carries RUSTSEC-2023-0071 (Marvin Attack). Not exploitable on our verify-only
# path (public key, no private-key ops); see deny.toml / issue #48.
rust_crypto = ["builtin_jwt", "jsonwebtoken/rust_crypto", "dep:rustls-rustcrypto"]
# `aws_lc_rs`: constant-time / FIPS-capable backend via aws-lc (C FFI),
# advisory-free. Opt-in for consumers that allow FFI; enable with
# `default-features = false, features = ["aws_lc_rs"]`.
aws_lc_rs = ["builtin_jwt", "jsonwebtoken/aws_lc_rs", "rustls/aws_lc_rs"]

# Shared Redis-backed rate-limit store for multi-instance deployments.
redis = ["dep:redis"]

# The `structured-proxy` binary. Off by default: the library links none of its
# dependencies unless asked. It carries `redis`, so `cargo install
# structured-proxy --features cli` builds what the release packages ship.
cli = ["dep:clap", "dep:tracing-subscriber", "redis"]

[dev-dependencies]
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
tower = { version = "0.5", features = ["util"] }
http-body-util = "0.1"
# Trailer frames for the hand-written upstream response in
# tests/upstream_controls.rs (tonic's server API cannot set success trailers).
http-body = "1"
# The local HTTPS server the outbound TLS client is tested against
# (src/tls/tests.rs), and the provider it runs on in every build. Default
# features would pull in aws-lc.
tokio-rustls = { version = "0.26", default-features = false, features = ["tls12"] }
rustls-rustcrypto = "0.0.2-alpha"
# benches/jwt_verify.rs. Plots and rayon are left out: numbers are enough.
criterion = { version = "0.8", default-features = false, features = ["async_tokio", "cargo_bench_support"] }
# The embedding-hooks integration test (tests/hooks.rs) writes hook impls using
# only these (the same crates a real embedder uses — none is an HTTP framework)
# and drives the resulting router via axum + tower for assertions.
axum = { version = "0.8", features = ["macros"] }
http = "1"
bytes = "1"
async-trait = "0.1"
ed25519-dalek = "3"
rand = "0.10"
chrono = "0.4"
tokio-stream = "0.1"
# Compiles the in-memory test protos (google.api.http routes) of the
# error-details integration test without a protoc binary.
protox = "0.9"

# Runs the built binary, so it needs the binary built.
[[test]]
name = "cli"
required-features = ["cli"]

# The built-in verifier's cost with and without the claims cache.
[[bench]]
name = "jwt_verify"
harness = false
required-features = ["builtin_jwt"]

# Building a gRPC request message from path, query and body.
[[bench]]
name = "transcode_request"
harness = false