Skip to main content

ProxyServer

Struct ProxyServer 

Source
pub struct ProxyServer { /* private fields */ }
Expand description

Universal proxy server.

Implementations§

Source§

impl ProxyServer

Source

pub fn from_config(config: ProxyConfig) -> Self

Create from YAML config file.

Source

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.

Source

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.

Source

pub fn config(&self) -> &ProxyConfig

The configuration this server was created with.

Source

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)?;
Source

pub fn with_upstream_address(self, address: impl Into<String>) -> Self

The remote gRPC upstream (upstream.default), for upstream and serve.

Source

pub fn with_listen(self, listen: ListenConfig) -> Self

The listener of serve: address, TLS, connection limit (listen:).

Source

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.

Source

pub fn with_aliases( self, aliases: impl IntoIterator<Item = AliasConfig>, ) -> Self

Path aliases rewritten before routing (aliases:).

Source

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.

Source

pub fn with_health(self, health: HealthConfig) -> Self

Health probe endpoints (health:).

Source

pub fn with_metrics(self, metrics: MetricsConfig) -> Self

The Prometheus metrics endpoint (metrics:).

Source

pub fn with_openapi(self, openapi: OpenApiConfig) -> Self

The OpenAPI spec and docs endpoints (openapi:).

Source

pub fn with_oidc_discovery(self, oidc: OidcDiscoveryConfig) -> Self

Static OIDC discovery and JWKS endpoints (oidc_discovery:).

Source

pub fn with_cors(self, cors: CorsConfig) -> Self

The CORS policy (cors:).

Source

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).

Source

pub fn with_streaming(self, streaming: StreamingConfig) -> Self

Server-streaming behavior (streaming:).

Source

pub fn with_maintenance(self, maintenance: MaintenanceConfig) -> Self

Maintenance mode (maintenance:).

Source

pub fn with_concurrency_limit(self, concurrency: ConcurrencyConfig) -> Self

The limit on requests in flight (concurrency:).

Source

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())?;
Source

pub fn with_rate_limits(self, shield: ShieldConfig) -> Self

Rate limits (shield:); build the section with config::from_yaml.

Source

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.

Source

pub fn with_descriptors(self, pool: DescriptorPool) -> Self

Create with an embedded descriptor pool (for sid-proxy backward compat).

Source

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.

Source

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]));
Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn with_ndjson_envelope(self, enabled: bool) -> Self

Wrap NDJSON stream lines in {"result"} / {"error"} envelopes; see transcode::TranscodeOptions::with_ndjson_envelope.

Source

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.

Source

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.

Source

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.

§Errors

No valid upstream address, or a configuration service rejects.

Source

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)?;
Source

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.

Source

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.

Source

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();
    })
    .await

Trait Implementations§

Source§

impl Default for ProxyServer

Source§

fn default() -> Self

ProxyServer::new: every capability off.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<L> LayerExt<L> for L

Source§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in Layered.
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more