diff --git a/Cargo.lock b/Cargo.lock index 0f3ab2988..bec79acd5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -134,6 +134,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener", +] + [[package]] name = "async-native-tls" version = "0.4.0" @@ -216,6 +225,54 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2cc6e8e8c993cb61a005fab8c1e5093a29199b7253b05a6883999312935c1ff" +dependencies = [ + "async-trait", + "axum-core", + "base64 0.13.0", + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa 1.0.2", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sha-1 0.10.0", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-http", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4d047478b986f14a13edad31a009e2e05cb241f9805d0d75e4cba4e129ad4d" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", +] + [[package]] name = "backtrace" version = "0.3.65" @@ -512,6 +569,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" +[[package]] +name = "convert_case" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" + [[package]] name = "core-foundation" version = "0.9.3" @@ -706,8 +769,28 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.10.2", + "darling_macro 0.10.2", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02" +dependencies = [ + "darling_core 0.14.1", + "darling_macro 0.14.1", ] [[package]] @@ -720,7 +803,35 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.9.3", + "syn", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn", +] + +[[package]] +name = "darling_core" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", "syn", ] @@ -730,7 +841,29 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ - "darling_core", + "darling_core 0.10.2", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core 0.13.4", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5" +dependencies = [ + "darling_core 0.14.1", "quote", "syn", ] @@ -822,6 +955,26 @@ dependencies = [ "uuid 1.1.2", ] +[[package]] +name = "deltachat-jsonrpc" +version = "1.86.0" +dependencies = [ + "anyhow", + "async-channel", + "axum", + "deltachat", + "env_logger 0.9.0", + "futures", + "log", + "num-traits", + "serde", + "serde_json", + "tempfile", + "tokio", + "typescript-type-def", + "yerpc", +] + [[package]] name = "deltachat_derive" version = "2.0.0" @@ -836,6 +989,7 @@ version = "1.87.0" dependencies = [ "anyhow", "deltachat", + "deltachat-jsonrpc", "human-panic", "libc", "num-traits", @@ -863,7 +1017,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0" dependencies = [ - "darling", + "darling 0.10.2", "derive_builder_core", "proc-macro2", "quote", @@ -876,7 +1030,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef" dependencies = [ - "darling", + "darling 0.10.2", "proc-macro2", "quote", "syn", @@ -1130,7 +1284,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" dependencies = [ "atty", - "humantime", + "humantime 1.3.0", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime 2.1.0", "log", "regex", "termcolor", @@ -1544,6 +1711,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + [[package]] name = "httparse" version = "1.7.1" @@ -1586,6 +1759,12 @@ dependencies = [ "quick-error 1.2.3", ] +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.19" @@ -1880,6 +2059,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +[[package]] +name = "matchit" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" + [[package]] name = "md-5" version = "0.9.1" @@ -2406,7 +2591,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" dependencies = [ - "env_logger", + "env_logger 0.7.1", "log", ] @@ -3045,6 +3230,15 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "1.5.0" @@ -3125,6 +3319,12 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strum" version = "0.24.1" @@ -3161,6 +3361,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" + [[package]] name = "synstructure" version = "0.12.6" @@ -3292,6 +3498,7 @@ dependencies = [ "once_cell", "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "winapi", @@ -3344,6 +3551,18 @@ dependencies = [ "xattr", ] +[[package]] +name = "tokio-tungstenite" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06cda1232a49558c46f8a504d5b93101d42c0bf7f911f12a105ba48168f821ae" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.3" @@ -3367,6 +3586,47 @@ dependencies = [ "serde", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" + [[package]] name = "tower-service" version = "0.3.2" @@ -3380,10 +3640,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" dependencies = [ "cfg-if", + "log", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing-core" version = "0.1.28" @@ -3444,6 +3717,25 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "tungstenite" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5" +dependencies = [ + "base64 0.13.0", + "byteorder", + "bytes", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha-1 0.10.0", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "twofish" version = "0.6.0" @@ -3461,6 +3753,30 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +[[package]] +name = "typescript-type-def" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b4d7f4a1cf8923b722bd56456e0dd560869a37fb849169f18b848f261f01b96" +dependencies = [ + "serde_json", + "typescript-type-def-derive", +] + +[[package]] +name = "typescript-type-def-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8b246d662eb07a8627513dc78bad0b79da6d05e8224ecd559963efc597af0" +dependencies = [ + "darling 0.13.4", + "ident_case", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-bidi" version = "0.3.8" @@ -3521,6 +3837,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8parse" version = "0.2.0" @@ -3809,6 +4131,41 @@ dependencies = [ "libc", ] +[[package]] +name = "yerpc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1baa6fce4cf16d1cff91b557baceac3e363106f66e555fff906a7f82dce8153" +dependencies = [ + "anyhow", + "async-channel", + "async-mutex", + "async-trait", + "axum", + "futures", + "futures-util", + "log", + "serde", + "serde_json", + "tokio", + "tracing", + "typescript-type-def", + "yerpc_derive", +] + +[[package]] +name = "yerpc_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f944ca6789bc55ddc86839478f6d49c9d2a66e130f69fd1f8d171b3108990" +dependencies = [ + "convert_case", + "darling 0.14.1", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zeroize" version = "1.5.6" diff --git a/deltachat-jsonrpc/Cargo.toml b/deltachat-jsonrpc/Cargo.toml index a7e337578..ff6dd6128 100644 --- a/deltachat-jsonrpc/Cargo.toml +++ b/deltachat-jsonrpc/Cargo.toml @@ -13,7 +13,6 @@ required-features = ["webserver"] [dependencies] anyhow = "1" -async-std = { version = "1", features = ["attributes"] } deltachat = { path = ".." } num-traits = "0.2" serde = { version = "1.0", features = ["derive"] } @@ -22,18 +21,21 @@ log = "0.4" async-channel = { version = "1.6.1" } futures = { version = "0.3.19" } serde_json = "1.0.75" -yerpc = { git = "https://github.com/Frando/yerpc", features = ["anyhow"] } -typescript-type-def = { git = "https://github.com/Frando/rust-typescript-type-def", branch = "yerpc", features = ["json_value"] } -# optional, depended on features +yerpc = { version = "0.3", features = ["anyhow"] } +typescript-type-def = { version = "0.5.3", features = ["json_value"] } +tokio = { version = "1.19.2" } + +# optional dependencies +axum = { version = "0.5.9", optional = true, features = ["ws"] } env_logger = { version = "0.9.0", optional = true } -tide = { version = "0.16.0", optional = true } -tide-websockets = { version = "0.4.0", optional = true } -yerpc-tide = { git = "https://github.com/Frando/yerpc", optional = true } + +[dev-dependencies] +tokio = { version = "1.19.2", features = ["full", "rt-multi-thread"] } [features] default = [] -webserver = ["env_logger", "tide", "tide-websockets", "yerpc-tide"] +webserver = ["env_logger", "axum", "tokio/full", "yerpc/support-axum"] [profile.release] lto = true diff --git a/deltachat-jsonrpc/src/api/mod.rs b/deltachat-jsonrpc/src/api/mod.rs index 3907101cf..20bdc29ce 100644 --- a/deltachat-jsonrpc/src/api/mod.rs +++ b/deltachat-jsonrpc/src/api/mod.rs @@ -1,5 +1,4 @@ use anyhow::{anyhow, bail, Context, Result}; -use async_std::sync::{Arc, RwLock}; use deltachat::{ chat::{get_chat_msgs, ChatId}, chatlist::Chatlist, @@ -10,7 +9,9 @@ use deltachat::{ provider::get_provider_info, }; use std::collections::BTreeMap; +use std::sync::Arc; use std::{collections::HashMap, str::FromStr}; +use tokio::sync::RwLock; use yerpc::rpc; pub use deltachat::accounts::Accounts; diff --git a/deltachat-jsonrpc/src/lib.rs b/deltachat-jsonrpc/src/lib.rs index bd5d30694..5509b4a3e 100644 --- a/deltachat-jsonrpc/src/lib.rs +++ b/deltachat-jsonrpc/src/lib.rs @@ -1,18 +1,16 @@ pub mod api; pub use api::events; - pub use yerpc; #[cfg(test)] mod tests { use super::api::{Accounts, CommandApi}; use async_channel::unbounded; - use async_std::task; use futures::StreamExt; use tempfile::TempDir; - use yerpc::{MessageHandle, RpcHandle}; + use yerpc::{RpcClient, RpcSession}; - #[async_std::test] + #[tokio::test(flavor = "multi_thread")] async fn basic_json_rpc_functionality() -> anyhow::Result<()> { // println!("{}", ""); let tmp_dir = TempDir::new().unwrap().path().into(); @@ -23,10 +21,10 @@ mod tests { let (sender, mut receiver) = unbounded::(); - let (request_handle, mut rx) = RpcHandle::new(); + let (client, mut rx) = RpcClient::new(); let session = cmd_api; - let handle = MessageHandle::new(request_handle, session); - task::spawn({ + let handle = RpcSession::new(client, session); + tokio::spawn({ async move { while let Some(message) = rx.next().await { let message = serde_json::to_string(&message)?; @@ -41,7 +39,7 @@ mod tests { { let request = r#"{"jsonrpc":"2.0","method":"add_account","params":[],"id":1}"#; let response = r#"{"jsonrpc":"2.0","id":1,"result":1}"#; - handle.handle_message(request).await; + handle.handle_incoming(request).await; let result = receiver.next().await; println!("{:?}", result); assert_eq!(result, Some(response.to_owned())); @@ -49,7 +47,7 @@ mod tests { { let request = r#"{"jsonrpc":"2.0","method":"get_all_account_ids","params":[],"id":2}"#; let response = r#"{"jsonrpc":"2.0","id":2,"result":[1]}"#; - handle.handle_message(request).await; + handle.handle_incoming(request).await; let result = receiver.next().await; println!("{:?}", result); assert_eq!(result, Some(response.to_owned())); diff --git a/deltachat-jsonrpc/src/webserver.rs b/deltachat-jsonrpc/src/webserver.rs index 21b62c472..d3dc7f743 100644 --- a/deltachat-jsonrpc/src/webserver.rs +++ b/deltachat-jsonrpc/src/webserver.rs @@ -1,44 +1,43 @@ -use async_std::path::PathBuf; -use async_std::task; -use tide::Request; -use yerpc::RpcHandle; -use yerpc_tide::yerpc_handler; +use axum::{extract::ws::WebSocketUpgrade, response::Response, routing::get, Extension, Router}; +use std::net::SocketAddr; +use std::path::PathBuf; +use yerpc::axum::handle_ws_rpc; +use yerpc::{RpcClient, RpcSession}; mod api; use api::events::event_to_json_rpc_notification; use api::{Accounts, CommandApi}; -#[async_std::main] +#[tokio::main] async fn main() -> Result<(), std::io::Error> { - env_logger::init(); - log::info!("Starting"); - - let accounts = Accounts::new(PathBuf::from("./accounts")).await.unwrap(); + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); + let path = std::env::var("DC_ACCOUNTS_PATH").unwrap_or_else(|_| "./accounts".to_string()); + log::info!("Starting with accounts directory `{path}`."); + let accounts = Accounts::new(PathBuf::from(&path)).await.unwrap(); let state = CommandApi::new(accounts); - - let mut app = tide::with_state(state.clone()); - app.at("/ws").get(yerpc_handler(request_handler)); - + let app = Router::new() + .route("/ws", get(handler)) + .layer(Extension(state.clone())); + let addr = SocketAddr::from(([127, 0, 0, 1], 20808)); state.accounts.read().await.start_io().await; - app.listen("127.0.0.1:20808").await?; + log::info!("JSON-RPC WebSocket server listening on {}", addr); + axum::Server::bind(&addr) + .serve(app.into_make_service()) + .await + .unwrap(); Ok(()) } -async fn request_handler( - request: Request, - rpc: RpcHandle, -) -> anyhow::Result { - let state = request.state().clone(); - task::spawn(event_loop(state.clone(), rpc)); - Ok(state) -} -async fn event_loop(state: CommandApi, rpc: RpcHandle) -> anyhow::Result<()> { - let events = state.accounts.read().await.get_event_emitter().await; - while let Some(event) = events.recv().await { - // log::debug!("event {:?}", event); - let event = event_to_json_rpc_notification(event); - rpc.notify("event", Some(event)).await?; - } - Ok(()) +async fn handler(ws: WebSocketUpgrade, Extension(api): Extension) -> Response { + let (client, out_receiver) = RpcClient::new(); + let session = RpcSession::new(client.clone(), api.clone()); + tokio::spawn(async move { + let events = api.accounts.read().await.get_event_emitter().await; + while let Some(event) = events.recv().await { + let event = event_to_json_rpc_notification(event); + client.send_notification("event", Some(event)).await.ok(); + } + }); + handle_ws_rpc(ws, out_receiver, session).await } diff --git a/deltachat-jsonrpc/typescript/package.json b/deltachat-jsonrpc/typescript/package.json index f6a34deaa..f1547e9b8 100644 --- a/deltachat-jsonrpc/typescript/package.json +++ b/deltachat-jsonrpc/typescript/package.json @@ -20,7 +20,7 @@ "dependencies": { "isomorphic-ws": "^4.0.1", "tiny-emitter": "git+https://github.com/Simon-Laux/tiny-emitter.git", - "yerpc": "^0.2.3" + "yerpc": "^0.3" }, "devDependencies": { "prettier": "^2.6.2",