pub struct ProxyServer { /* private fields */ }Expand description
Universal proxy server.
Implementations§
Source§impl ProxyServer
impl ProxyServer
Sourcepub fn from_config(config: ProxyConfig) -> Self
pub fn from_config(config: ProxyConfig) -> Self
Create from YAML config file.
Sourcepub fn from_yaml_str(yaml: &str) -> Result<Self>
pub fn from_yaml_str(yaml: &str) -> Result<Self>
Create from a YAML document: the ProxyConfig plus the transcoding
settings it does not hold (error_details:,
streaming.ndjson_envelope and response_headers:), applied as
with_error_details, with_ndjson_envelope and
with_denied_response_headers would. A top-level or streaming: key
no setting reads is logged as a warning.
§Errors
Invalid YAML, a ProxyConfig that fails
validate, an error_details route pattern
that is relative or not a valid glob, or a response_headers.deny
entry that is not a header name.
Sourcepub fn from_file(path: &Path) -> Result<Self>
pub fn from_file(path: &Path) -> Result<Self>
from_yaml_str on the contents of a file.
§Errors
The file cannot be read, or its contents are rejected by
from_yaml_str.
Sourcepub fn config(&self) -> &ProxyConfig
pub fn config(&self) -> &ProxyConfig
The configuration this server was created with.
Sourcepub fn new() -> Self
pub fn new() -> Self
A proxy with every capability off: native gRPC and gRPC-Web pass to
the upstream, every other request to the fallback (404 by default).
Turn on what you need with the methods below; each sets the config
section of the same name, so code and YAML describe one proxy.
§Examples
use structured_proxy::config::{ConcurrencyConfig, ScopeConfig, Traffic};
use structured_proxy::ProxyServer;
// Your gRPC API, with at most 1000 calls in flight and nothing else.
let grpc = tonic::service::Routes::default();
let service = ProxyServer::new()
.with_concurrency_limit(
ConcurrencyConfig::new(1000).with_scope(ScopeConfig::traffic([Traffic::Grpc])),
)
.service(grpc)?;Sourcepub fn with_upstream_address(self, address: impl Into<String>) -> Self
pub fn with_upstream_address(self, address: impl Into<String>) -> Self
Sourcepub fn with_listen(self, listen: ListenConfig) -> Self
pub fn with_listen(self, listen: ListenConfig) -> Self
The listener of serve: address, TLS, connection
limit (listen:).
Sourcepub fn with_transcoded_rpcs(
self,
names: impl IntoIterator<Item = impl Into<String>>,
) -> Self
pub fn with_transcoded_rpcs( self, names: impl IntoIterator<Item = impl Into<String>>, ) -> Self
Transcode only these services (package.Service) and methods
(package.Service/Method) of the descriptors (transcode.only); every
annotated RPC by default. A name the descriptors do not hold fails the
build.
Sourcepub fn with_aliases(
self,
aliases: impl IntoIterator<Item = AliasConfig>,
) -> Self
pub fn with_aliases( self, aliases: impl IntoIterator<Item = AliasConfig>, ) -> Self
Path aliases rewritten before routing (aliases:).
Sourcepub fn with_forwarded_headers(
self,
names: impl IntoIterator<Item = impl Into<String>>,
) -> Self
pub fn with_forwarded_headers( self, names: impl IntoIterator<Item = impl Into<String>>, ) -> Self
The request headers transcoded calls forward as gRPC metadata
(forwarded_headers:), replacing the default list.
Sourcepub fn with_health(self, health: HealthConfig) -> Self
pub fn with_health(self, health: HealthConfig) -> Self
Health probe endpoints (health:).
Sourcepub fn with_metrics(self, metrics: MetricsConfig) -> Self
pub fn with_metrics(self, metrics: MetricsConfig) -> Self
The Prometheus metrics endpoint (metrics:).
Sourcepub fn with_openapi(self, openapi: OpenApiConfig) -> Self
pub fn with_openapi(self, openapi: OpenApiConfig) -> Self
The OpenAPI spec and docs endpoints (openapi:).
Sourcepub fn with_oidc_discovery(self, oidc: OidcDiscoveryConfig) -> Self
pub fn with_oidc_discovery(self, oidc: OidcDiscoveryConfig) -> Self
Static OIDC discovery and JWKS endpoints (oidc_discovery:).
Sourcepub fn with_cors(self, cors: CorsConfig) -> Self
pub fn with_cors(self, cors: CorsConfig) -> Self
The CORS policy (cors:).
Sourcepub fn with_grpc_web_translation(self, translate: bool) -> Self
pub fn with_grpc_web_translation(self, translate: bool) -> Self
Translate gRPC-Web to gRPC for an upstream that speaks only gRPC
(grpc_web.translate).
Sourcepub fn with_streaming(self, streaming: StreamingConfig) -> Self
pub fn with_streaming(self, streaming: StreamingConfig) -> Self
Server-streaming behavior (streaming:).
Sourcepub fn with_maintenance(self, maintenance: MaintenanceConfig) -> Self
pub fn with_maintenance(self, maintenance: MaintenanceConfig) -> Self
Maintenance mode (maintenance:).
Sourcepub fn with_concurrency_limit(self, concurrency: ConcurrencyConfig) -> Self
pub fn with_concurrency_limit(self, concurrency: ConcurrencyConfig) -> Self
The limit on requests in flight (concurrency:).
Sourcepub fn with_client_address(self, client_address: ClientAddressConfig) -> Self
pub fn with_client_address(self, client_address: ClientAddressConfig) -> Self
How the client’s address is resolved (client_address:): the proxies
trusted to report it and the header they use. Every consumer reads the
result, the rate limits as much as the upstream.
§Examples
use structured_proxy::config::{ClientAddressConfig, ForwardingHeader};
use structured_proxy::ProxyServer;
// Behind a load balancer in 10.0.0.0/8 that appends to X-Forwarded-For.
let mut client_address = ClientAddressConfig::default();
client_address.trusted_proxies = vec!["10.0.0.0/8".into()];
client_address.header = ForwardingHeader::XForwardedFor;
client_address.required = true;
let service = ProxyServer::new()
.with_client_address(client_address)
.service(tonic::service::Routes::default())?;Sourcepub fn with_rate_limits(self, shield: ShieldConfig) -> Self
pub fn with_rate_limits(self, shield: ShieldConfig) -> Self
Rate limits (shield:); build the section with config::from_yaml.
Sourcepub fn with_auth(self, auth: AuthConfig) -> Self
pub fn with_auth(self, auth: AuthConfig) -> Self
JWT auth, route policies, forward-auth and ext_authz (auth:); build
the section with config::from_yaml.
Sourcepub fn with_descriptors(self, pool: DescriptorPool) -> Self
pub fn with_descriptors(self, pool: DescriptorPool) -> Self
Create with an embedded descriptor pool (for sid-proxy backward compat).
Sourcepub fn with_auth_decider(self, decider: Arc<dyn AuthDecider>) -> Self
pub fn with_auth_decider(self, decider: Arc<dyn AuthDecider>) -> Self
Inject an in-process forward-auth / PDP decision (embedded Tier-2 hook).
The decider gates the transcoded requests inline (other traffic with
with_auth_decider_scope) and also
backs the /verify forward-auth endpoint. Its signature is axum-free
(see hooks::AuthDecider), so the embedder never names an HTTP
framework.
Sourcepub fn with_auth_decider_scope(self, scope: ScopeConfig) -> Self
pub fn with_auth_decider_scope(self, scope: ScopeConfig) -> Self
Choose the traffic the injected AuthDecider gates, transcoded
by default; the /verify endpoint is never behind it, since it answers
for the decider.
§Examples
use structured_proxy::config::{ScopeConfig, Traffic};
use structured_proxy::ProxyServer;
// Gate native gRPC calls as well as the transcoded ones.
let server = ProxyServer::from_yaml_str("service:\n name: demo\n")?
.with_auth_decider_scope(ScopeConfig::traffic([Traffic::Transcoded, Traffic::Grpc]));Sourcepub fn with_oidc_backend(self, backend: Arc<dyn OidcBackend>) -> Self
pub fn with_oidc_backend(self, backend: Arc<dyn OidcBackend>) -> Self
Back the stateless OIDC surface (discovery, JWKS, userinfo) with the embedder’s key/client metadata (embedded Tier-2 hook).
When set, this supersedes the config-driven static oidc_discovery
routes. See hooks::OidcBackend.
Sourcepub fn with_extra_routes(
self,
routes: impl IntoIterator<Item = ExtraRoute>,
) -> Self
pub fn with_extra_routes( self, routes: impl IntoIterator<Item = ExtraRoute>, ) -> Self
Register extra stateless routes through an axum-free adapter (embedded
Tier-2 hook). See hooks::ExtraRoute / hooks::ExtraRouteHandler.
Sourcepub fn with_verify_path(self, path: impl Into<String>) -> Self
pub fn with_verify_path(self, path: impl Into<String>) -> Self
Set the path at which the injected AuthDecider answers forward-auth
sub-requests (/verify). Independent of any JWT forward_auth config, so
a decider-only embedder can place it without a JWT block.
Resolution order for the path: this override, then
auth.forward_auth.path from config, then the default /auth/verify.
Sourcepub fn with_token_verifier(self, verifier: Arc<dyn TokenVerifier>) -> Self
pub fn with_token_verifier(self, verifier: Arc<dyn TokenVerifier>) -> Self
Verify bearer tokens with the embedder’s own verifier instead of the built-in one (embedded Tier-2 hook).
The JWT middleware keeps everything around the signature check — route
policies, the roles claim, claim→header forwarding — and takes the
verdict from hooks::TokenVerifier. Use this when the built-in crypto
backend is not the one this binary needs: a validated / FIPS module, an
HSM, or a verifier the embedder already owns. It is also the way out of
Cargo’s feature unification, which makes rust_crypto / aws_lc_rs a
property of the whole dependency graph rather than of one binary.
auth.mode must still be "jwt" for the middleware to run; auth.jwt
then only configures claim forwarding, and any key source in it is
ignored (with a warning), since the verifier owns its own keys.
Sourcepub fn with_error_details(self, policy: ErrorDetailsPolicy) -> Self
pub fn with_error_details(self, policy: ErrorDetailsPolicy) -> Self
Choose which transcoded routes return the upstream’s
google.rpc.Status details in their error bodies. Every route does by
default; see transcode::error::ErrorDetailsPolicy to switch them
off globally or per route.
Sourcepub fn with_ndjson_envelope(self, enabled: bool) -> Self
pub fn with_ndjson_envelope(self, enabled: bool) -> Self
Wrap NDJSON stream lines in {"result"} / {"error"} envelopes; see
transcode::TranscodeOptions::with_ndjson_envelope.
Sourcepub fn with_denied_response_headers(
self,
names: impl IntoIterator<Item = HeaderName>,
) -> Self
pub fn with_denied_response_headers( self, names: impl IntoIterator<Item = HeaderName>, ) -> Self
Keep these upstream response metadata keys off the HTTP responses of
the transcoded routes; see
transcode::TranscodeOptions::with_denied_response_headers.
Sourcepub fn upstream(&self) -> Result<Channel>
pub fn upstream(&self) -> Result<Channel>
A lazy channel to the configured upstream address (upstream.default),
the upstream of the standalone proxy. It connects on first use, giving
up after five seconds.
§Errors
No upstream address is configured, or it is not a valid URI.
Sourcepub fn router(&self) -> Result<Router>
pub fn router(&self) -> Result<Router>
The proxy’s HTTP routes in front of the configured upstream address
(see upstream), as an axum Router to serve or to
merge into an axum application. It answers HTTP only; use
service for native gRPC on the same listener, or for
an upstream in process.
A URL whose path a transcoded route matches but which no binding
answers (its field template or custom verb does not fit) goes to the
proxy’s other routes, extra routes included, and is answered 404
when none of them answers it. An axum router routes a request to one
route, so routes merged after this one are not tried for it: serve the
application’s own routes through
with_extra_routes, or use
service with
with_fallback.
§Errors
No valid upstream address, or a configuration service
rejects.
Sourcepub fn service<U: Upstream>(&self, upstream: U) -> Result<ProxyService<U>>
pub fn service<U: Upstream>(&self, upstream: U) -> Result<ProxyService<U>>
The whole proxy as one tower service in front of upstream: native
gRPC requests reach upstream unchanged, every other request the
proxy’s routes, whose transcoded calls go to upstream too. See
ProxyService.
upstream is any gRPC service (upstream::Upstream): the embedder’s
own tonic services in process, with no socket between them and the
proxy, or a remote Channel such as
upstream.
§Errors
An invalid configuration (see ProxyConfig::validate), descriptors
that cannot be loaded, a route mounted twice, or a malformed auth,
authz, shield or OIDC section.
§Examples
use structured_proxy::ProxyServer;
let grpc = tonic::service::Routes::default(); // add your services here
let service = ProxyServer::from_yaml_str("service:\n name: demo\n")?.service(grpc)?;Sourcepub fn serve_options(&self) -> Result<ServeOptions>
pub fn serve_options(&self) -> Result<ServeOptions>
The ServeOptions of listen:: TLS from listen.tls (mTLS with its
client_ca_file), the listen.max_connections cap and the connection
timeouts, for serve_with on a listener of your own.
§Errors
A max_connections, header_read_timeout_secs or
tls.handshake_timeout_secs of zero, TLS files that cannot be loaded,
or no rustls crypto provider for TLS.
Sourcepub async fn serve(&self) -> Result<()>
pub async fn serve(&self) -> Result<()>
Serve the proxy on the configured listen address in front of the
configured upstream address: REST and native gRPC on one port, with
the TLS and connection limit of listen: (see
serve_options and serve_with).
§Errors
What upstream, service and
serve_options reject, an invalid listen
address, or a listener that fails.
Sourcepub async fn serve_with_shutdown(
&self,
signal: impl Future<Output = ()>,
) -> Result<()>
pub async fn serve_with_shutdown( &self, signal: impl Future<Output = ()>, ) -> Result<()>
serve until signal completes, then shut down with
the drain of listen.drain_timeout_secs (see serve_with_shutdown).
§Errors
What serve returns.
§Examples
server
.serve_with_shutdown(async {
tokio::signal::ctrl_c().await.ok();
})
.awaitTrait Implementations§
Source§impl Default for ProxyServer
impl Default for ProxyServer
Source§fn default() -> Self
fn default() -> Self
ProxyServer::new: every capability off.
Auto Trait Implementations§
impl !RefUnwindSafe for ProxyServer
impl !UnwindSafe for ProxyServer
impl Freeze for ProxyServer
impl Send for ProxyServer
impl Sync for ProxyServer
impl Unpin for ProxyServer
impl UnsafeUnpin for ProxyServer
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request