Skip to content

Commit

Permalink
use httpz + localhost server for Linux
Browse files Browse the repository at this point in the history
  • Loading branch information
oscartbeaumont committed Feb 7, 2023
1 parent 1e3b645 commit 0a090c9
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 100 deletions.
24 changes: 22 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ prisma-client-rust-sdk = { git = "https://github.com/Brendonovich/prisma-client-

rspc = { version = "0.1.2" }
specta = { version = "0.0.6" }
httpz = { version = "0.0.3" }

swift-rs = { git = "https://github.com/Brendonovich/swift-rs.git", rev = "833e29ba333f1dfe303eaa21de78c4f8c5a3f2ff" }

Expand All @@ -41,3 +42,4 @@ openssl-sys = { git = "https://github.com/spacedriveapp/rust-openssl", rev = "92

rspc = { git = "https://github.com/oscartbeaumont/rspc", rev = "c03872c0ba29d2429e9c059dfb235cdd03e15e8c" } # TODO: Move back to crates.io when new jsonrpc executor + `tokio::spawn` in the Tauri IPC plugin + upgraded Tauri version is released
specta = { git = "https://github.com/oscartbeaumont/rspc", rev = "c03872c0ba29d2429e9c059dfb235cdd03e15e8c" }
httpz = { git = "https://github.com/oscartbeaumont/httpz", rev = "a5185f2ed2fdefeb2f582dce38a692a1bf76d1d6" }
5 changes: 5 additions & 0 deletions apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ build = "build.rs"
[dependencies]
tauri = { version = "1.2.4", features = ["api-all", "linux-protocol-headers", "macos-private-api"] }
rspc = { workspace = true, features = ["tauri"] }
httpz = { workspace = true, features = ["axum", "tauri"] } # TODO: The `axmu` feature should be only enabled on Linux but this currently can't be done: https://github.com/rust-lang/cargo/issues/1197
sd-core = { path = "../../../core", features = ["ffmpeg", "location-watcher"] }
tokio = { workspace = true, features = ["sync"] }
window-shadows = "0.2.0"
Expand All @@ -20,6 +21,10 @@ serde = "1.0.145"
percent-encoding = "2.2.0"
http = "0.2.8"

[target.'cfg(target_os = "linux")'.dependencies]
axum = "0.6.4"
server = { path = "../../server" }

[target.'cfg(target_os = "macos")'.dependencies]
swift-rs.workspace = true

Expand Down
105 changes: 60 additions & 45 deletions apps/desktop/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
)]

use std::error::Error;
use std::net::SocketAddr;
use std::path::PathBuf;
use std::time::Duration;

use http::Request;
use sd_core::{custom_uri::handle_custom_uri, Node};
use tauri::async_runtime::block_on;
use tauri::{api::path, http::ResponseBuilder, Manager, RunEvent};
use tokio::task::block_in_place;
use tokio::time::sleep;
use sd_core::{custom_uri::create_custom_uri_endpoint, Node};
use tauri::plugin::TauriPlugin;
use tauri::Runtime;
use tauri::{api::path, async_runtime::block_on, Manager, RunEvent};
use tokio::{task::block_in_place, time::sleep};
use tracing::{debug, error};

#[cfg(target_os = "macos")]
Expand All @@ -27,6 +27,15 @@ async fn app_ready(app_handle: tauri::AppHandle) {
window.show().unwrap();
}

pub fn spacedrive_plugin_init<R: Runtime>(listen_addr: SocketAddr) -> TauriPlugin<R> {
tauri::plugin::Builder::new("spacedrive")
.js_init_script(format!(
r#"window.__SD_CUSTOM_URI_SERVER__ = "http://{}";"#,
listen_addr
))
.build()
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let data_dir = path::data_dir()
Expand All @@ -35,44 +44,48 @@ async fn main() -> Result<(), Box<dyn Error>> {

let (node, router) = Node::new(data_dir).await?;

let app = tauri::Builder::default()
.plugin(rspc::integrations::tauri::plugin(router, {
let node = node.clone();
move || node.get_request_context()
}))
.register_uri_scheme_protocol("spacedrive", {
let node = node.clone();
move |_, req| {
let uri = req.uri();
let uri = uri
.replace("spacedrive://localhost/", "http://spacedrive.localhost/") // Windows
.replace("spacedrive://", "http://spacedrive.localhost/"); // Unix style

// Encoded by `convertFileSrc` on the frontend
let uri = percent_encoding::percent_decode(uri.as_bytes())
.decode_utf8_lossy()
.to_string();

let mut r = Request::builder().method(req.method()).uri(uri);
for (key, value) in req.headers() {
r = r.header(key, value);
}
let r = r.body(req.body().clone()).unwrap(); // TODO: This clone feels so unnecessary but Tauri pass `req` as a reference so we can get the owned value.

// TODO: This blocking sucks but is required for now. https://github.com/tauri-apps/wry/issues/420
let resp = block_in_place(|| block_on(handle_custom_uri(&node, r)))
.unwrap_or_else(|err| err.into_response().unwrap());
let mut r = ResponseBuilder::new()
.version(resp.version())
.status(resp.status());

for (key, value) in resp.headers() {
r = r.header(key, value);
}

r.body(resp.into_body())
}
})
let app = tauri::Builder::default().plugin(rspc::integrations::tauri::plugin(router, {
let node = node.clone();
move || node.get_request_context()
}));

// This is a super cringe workaround for: https://github.com/tauri-apps/tauri/issues/3725 & https://bugs.webkit.org/show_bug.cgi?id=146351#c5
// TODO: Secure this server against other apps on the users machine making requests to it using a HTTP header and random token or something
let endpoint = create_custom_uri_endpoint(node.clone());
#[cfg(target_os = "linux")]
let app = {
use axum::routing::get;
use std::net::TcpListener;

let signal = server::utils::axum_shutdown_signal(node.clone());

let axum_app = axum::Router::new()
.route("/", get(|| async { "Spacedrive Server!" }))
.nest("/spacedrive", endpoint.axum())
.fallback(|| async { "404 Not Found: We're past the event horizon..." });

let listener = TcpListener::bind("127.0.0.1:0").expect("Error creating localhost server!"); // Only allow current device to access it and randomise port
let listen_addr = listener
.local_addr()
.expect("Error getting localhost server listen addr!");
debug!("Localhost server listening on: http://{:?}", listen_addr);

tokio::spawn(async move {
axum::Server::from_tcp(listener)
.expect("error creating HTTP server!")
.serve(axum_app.into_make_service())
.with_graceful_shutdown(signal)
.await
.expect("Error with HTTP server!");
});

app.plugin(spacedrive_plugin_init(listen_addr))
};

#[cfg(not(target_os = "linux"))]
let app = app.register_uri_scheme_protocol("spacedrive", endpoint.tauri_uri_scheme("spacedrive"));

let app = app
.setup(|app| {
let app = app.handle();
app.windows().iter().for_each(|(_, window)| {
Expand All @@ -83,7 +96,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
async move {
sleep(Duration::from_secs(3)).await;
if !window.is_visible().unwrap_or(true) {
println!("Window did not emit `app_ready` event fast enough. Showing window...");
println!(
"Window did not emit `app_ready` event fast enough. Showing window..."
);
let _ = window.show();
}
}
Expand Down
19 changes: 17 additions & 2 deletions apps/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,26 @@ async function getOs(): Promise<OperatingSystem> {
}
}

let customUriServerUrl = (window as any).__SD_CUSTOM_URI_SERVER__ as string | undefined;

if (customUriServerUrl && !customUriServerUrl?.endsWith('/')) {
customUriServerUrl += '/';
}

function getCustomUriURL(path: string): string {
if (customUriServerUrl) {
console.log(customUriServerUrl, path);
return customUriServerUrl + 'spacedrive/' + path;
} else {
return convertFileSrc(path, 'spacedrive');
}
}

const platform: Platform = {
platform: 'tauri',
getThumbnailUrlById: (casId) => convertFileSrc(`thumbnail/${casId}`, 'spacedrive'),
getThumbnailUrlById: (casId) => getCustomUriURL(`thumbnail/${casId}`),
getFileUrl: (libraryId, locationLocalId, filePathId) =>
convertFileSrc(`file/${libraryId}/${locationLocalId}/${filePathId}`, 'spacedrive'),
getCustomUriURL(`file/${libraryId}/${locationLocalId}/${filePathId}`),
openLink: shell.open,
getOs,
openDirectoryPickerDialog: () => dialog.open({ directory: true }),
Expand Down
1 change: 1 addition & 0 deletions apps/server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition = "2021"
[dependencies]
sd-core = { path = "../../core", features = ["ffmpeg"] }
rspc = { workspace = true, features = ["axum"] }
httpz = { workspace = true, features = ["axum"] }
axum = "0.6.4"
tokio = { workspace = true, features = ["sync", "rt-multi-thread", "signal"] }
tracing = "0.1.36"
Expand Down
1 change: 1 addition & 0 deletions apps/server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod utils;
46 changes: 6 additions & 40 deletions apps/server/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
use std::{env, net::SocketAddr, path::Path};

use axum::{
body::{Body, Full},
http::Request,
response::Response,
routing::get,
};
use hyper::body::to_bytes;
use sd_core::{custom_uri::handle_custom_uri, Node};
use axum::routing::get;
use sd_core::{custom_uri::create_custom_uri_endpoint, Node};
use tracing::info;

mod utils;
Expand Down Expand Up @@ -40,38 +34,10 @@ async fn main() {
let app = axum::Router::new()
.route("/", get(|| async { "Spacedrive Server!" }))
.route("/health", get(|| async { "OK" }))
.route("/spacedrive/*id", {
let node = node.clone();
get(|req: Request<Body>| async move {
let (parts, body) = req.into_parts();
let mut r =
Request::builder().method(parts.method).uri(
parts.uri.path().strip_prefix("/spacedrive").expect(
"Error decoding Spacedrive URL prefix. This should be impossible!",
),
);
for (key, value) in parts.headers {
if let Some(key) = key {
r = r.header(key, value);
}
}
let r = r.body(to_bytes(body).await.unwrap().to_vec()).unwrap();

let resp = handle_custom_uri(&node, r)
.await
.unwrap_or_else(|err| err.into_response().unwrap());

let mut r = Response::builder()
.version(resp.version())
.status(resp.status());

for (key, value) in resp.headers() {
r = r.header(key, value);
}

r.body(Full::from(resp.into_body())).unwrap()
})
})
.nest(
"/spacedrive",
create_custom_uri_endpoint(node.clone()).axum(),
)
.nest(
"/rspc",
router.endpoint(move || node.get_request_context()).axum(),
Expand Down
2 changes: 1 addition & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ blake3 = "1.3.1"

# Project dependencies
rspc = { workspace = true, features = ["uuid", "chrono", "tracing"] }
httpz = { workspace = true }
prisma-client-rust = { workspace = true }
specta = { workspace = true }
uuid = { version = "1.1.2", features = ["v4", "serde"] }
Expand Down Expand Up @@ -68,7 +69,6 @@ notify = { version = "5.0.0", default-features = false, features = [
"macos_fsevent",
], optional = true }
uhlc = "0.5.1"
http = "0.2.8"
http-range = "0.1.5"
mini-moka = "0.10.0"
serde_with = "2.2.0"
Expand Down
Loading

0 comments on commit 0a090c9

Please sign in to comment.