1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//! 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](StorageProxyBackend):
//! 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
//!
//! ```text
//! 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:
//!
//! ```
//! # #[cfg(feature = "testing")] {
//! 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);
//! # let _ = proxy;
//! # }
//! ```
pub use ;
pub use ;
pub use UnityFactoryProxyBackend;
pub use ;
pub use StorageProxyHandler;
pub use ;