Expand description
Same-origin Unity Catalog storage byte-proxy.
A browser/wasm query engine points a stock
object_store::http::HttpStore at this proxy instead of reaching cloud
object storage directly. The proxy authorizes the request, vends a scoped
credential, and streams object bytes (read + write) to/from storage
server-side. Because the wasm side never speaks a cloud-native protocol
(SigV4 / Azure SAS / GCS auth) this makes S3/GCP work on wasm for free and
removes the per-cloud CORS configuration burden — the browser only ever talks
to its own origin.
The crate owns the proxy semantics behind a narrow port:
the wire contract (ranged GET / HEAD / whole-object PUT with a
server-enforced If-Match), the streaming HTTP mapping, and the
confused-deputy path-scope guard. Any server serves the identical surface by
implementing StorageProxyBackend over its own storage, credential vending,
and authorization.
§Wire contract
GET {base}/{securable}/{key} -> 200 + body + Content-Length + ETag
GET {base}/{securable}/{key} Range: .. -> 206 + Content-Range (+ Accept-Ranges)
HEAD {base}/{securable}/{key} -> headers only
PUT {base}/{securable}/{key} [body] -> 200 + ETag (whole-object upload){securable} is a single typed path segment — table:<fqn>, vol:<fqn>, or
path:<percent-encoded cloud URL> (see Securable) — and {key} is the
object key relative to that securable’s root. GET/HEAD open a read-scoped
store; PUT opens a write-scoped store. A conditional write rides an
If-Match HTTP header the proxy relays to storage, mapping a mismatch to
412 Precondition Failed.
§Example
Implement StorageProxyBackend over your own storage, then mount it with
router_with_context. Here the in-memory backend from the testing feature
stands in for a real one:
use std::sync::Arc;
use unitycatalog_storage_proxy::{ContextExtractor, router_with_context_at};
use unitycatalog_storage_proxy::testing::InMemoryStorageProxyBackend;
let backend = InMemoryStorageProxyBackend::table("main.default.events");
let extract_cx: ContextExtractor<()> = Arc::new(|_parts| Box::pin(async { Ok(()) }));
// `base = ""` yields relative routes; a host adds the `/storage-proxy` prefix
// via `.nest`. Pass `"/storage-proxy"` instead to `.merge` the surface directly.
let proxy: axum::Router = router_with_context_at("", Arc::new(backend), extract_cx);Re-exports§
pub use auth::AuthLayer;binpub use auth::AuthMode;binpub use auth::ForwardedIdentity;binpub use backend::ForwardedUser;serverpub use backend::ProxyCapabilities;serverpub use backend::ProxyReq;serverpub use backend::ProxyVerb;serverpub use backend::Securable;serverpub use backend::StorageProxyBackend;serverpub use client::UnityFactoryProxyBackend;client-armpub use error::ProxyError;serverpub use error::ProxyResult;serverpub use handler::StorageProxyHandler;serverpub use router::ContextExtractor;serverpub use router::router_from_extension;serverpub use router::router_from_extension_at;serverpub use router::router_with_context;serverpub use router::router_with_context_at;server
Modules§
- auth
bin - Self-contained request authentication for the standalone
storage-proxybinary. - backend
server - The
StorageProxyBackendport and its request vocabulary. - cli
bin - CLI surface for the standalone
storage-proxybinary. - client
client-arm - The portable client arm: a
StorageProxyBackendbacked by aUnityObjectStoreFactory. - config
server - The
storageAccesscapability helper. - error
server - The storage-proxy error contract, decoupled from any server’s internal error.
- handler
server - The streaming HTTP handler: a blanket
StorageProxyHandlerover anyStorageProxyBackend. - router
server - Axum router for the storage byte-proxy (
/storage-proxy/{securable}/{*key}). - testing
testing - An in-memory
StorageProxyBackendfor exercising the wire contract without a cloud.