Skip to content

Commit

Permalink
feat: watch rpc types; http client (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
heilhead authored Jun 12, 2023
1 parent 68bcc3d commit 21bae4e
Show file tree
Hide file tree
Showing 24 changed files with 1,667 additions and 578 deletions.
15 changes: 9 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ members = [
]

[features]
full = [
"client",
"rpc",
]
default = ["full"]
full = ["client", "rpc"]
client = ["dep:relay_client"]
rpc = ["dep:relay_rpc"]

Expand All @@ -28,7 +26,12 @@ relay_rpc = { path = "./relay_rpc", optional = true }
anyhow = "1"
structopt = { version = "0.3", default-features = false }
tokio = { version = "1.22", features = ["full"] }
url = "2.3"

[[example]]
name = "websocket_client"
required-features = ["client","rpc"]

[[example]]
name = "basic_client"
required-features = ["full"]
name = "http_client"
required-features = ["client","rpc"]
72 changes: 72 additions & 0 deletions examples/http_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use {
relay_client::{http::Client, ConnectionOptions},
relay_rpc::{
auth::{ed25519_dalek::Keypair, rand, AuthToken},
domain::Topic,
},
std::{sync::Arc, time::Duration},
structopt::StructOpt,
url::Url,
};

#[derive(StructOpt)]
struct Args {
/// Specify HTTP address.
#[structopt(short, long, default_value = "https://relay.walletconnect.com/rpc")]
address: String,

/// Specify WalletConnect project ID.
#[structopt(short, long, default_value = "3cbaa32f8fbf3cdcc87d27ca1fa68069")]
project_id: String,
}

fn create_conn_opts(key: &Keypair, address: &str, project_id: &str) -> ConnectionOptions {
let aud = Url::parse(address)
.unwrap()
.origin()
.unicode_serialization();

let auth = AuthToken::new("http://example.com")
.aud(aud)
.ttl(Duration::from_secs(60 * 60))
.as_jwt(key)
.unwrap();

ConnectionOptions::new(project_id, auth).with_address(address)
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let args = Args::from_args();

let key1 = Keypair::generate(&mut rand::thread_rng());
let client1 = Client::new(&create_conn_opts(&key1, &args.address, &args.project_id))?;

let key2 = Keypair::generate(&mut rand::thread_rng());
let client2 = Client::new(&create_conn_opts(&key2, &args.address, &args.project_id))?;

let topic = Topic::generate();
let message: Arc<str> = Arc::from("Hello WalletConnect!");

client1
.publish(
topic.clone(),
message.clone(),
1100,
Duration::from_secs(30),
)
.await?;

println!("[client1] published message with topic: {topic}",);

tokio::time::sleep(Duration::from_secs(1)).await;

let messages = client2.fetch(topic).await?.messages;
let message = messages
.get(0)
.ok_or(anyhow::anyhow!("fetch did not return any messages"))?;

println!("[client2] received message: {}", message.message);

Ok(())
}
19 changes: 9 additions & 10 deletions examples/basic_client.rs → examples/websocket_client.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
use {
relay_client::{
Client,
CloseFrame,
ConnectionHandler,
error::Error,
websocket::{Client, CloseFrame, ConnectionHandler, PublishedMessage},
ConnectionOptions,
Error,
PublishedMessage,
},
relay_rpc::{
auth::{ed25519_dalek::Keypair, rand, AuthToken},
domain::{AuthSubject, Topic},
domain::Topic,
},
std::{sync::Arc, time::Duration},
structopt::StructOpt,
Expand Down Expand Up @@ -64,7 +61,7 @@ impl ConnectionHandler for Handler {
fn create_conn_opts(address: &str, project_id: &str) -> ConnectionOptions {
let key = Keypair::generate(&mut rand::thread_rng());

let auth = AuthToken::new(AuthSubject::generate())
let auth = AuthToken::new("http://example.com")
.aud(address)
.ttl(Duration::from_secs(60 * 60))
.as_jwt(&key)
Expand All @@ -79,12 +76,12 @@ async fn main() -> anyhow::Result<()> {

let client1 = Client::new(Handler::new("client1"));
client1
.connect(create_conn_opts(&args.address, &args.project_id))
.connect(&create_conn_opts(&args.address, &args.project_id))
.await?;

let client2 = Client::new(Handler::new("client2"));
client2
.connect(create_conn_opts(&args.address, &args.project_id))
.connect(&create_conn_opts(&args.address, &args.project_id))
.await?;

let topic = Topic::generate();
Expand All @@ -101,7 +98,9 @@ async fn main() -> anyhow::Result<()> {
)
.await?;

println!("[client2] published message with topic: {topic}");
println!("[client2] published message with topic: {topic}",);

tokio::time::sleep(Duration::from_millis(500)).await;

drop(client1);
drop(client2);
Expand Down
16 changes: 11 additions & 5 deletions relay_client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,20 @@ rustls = ["tokio-tungstenite/rustls-tls-native-roots"]
relay_rpc = { path = "../relay_rpc" }
futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] }
thiserror = "1.0"
tokio = { version = "1.22", features = ["rt", "time", "sync", "macros", "rt-multi-thread"] }
tokio-tungstenite = "0.18"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_qs = "0.10"
futures-channel = "0.3"
tokio-stream = "0.1"
tokio-util = "0.7"
pin-project = "1.0"
chrono = { version = "0.4", default-features = false, features = ["alloc", "std"] }
url = "2.3"
http = "0.2"

# HTTP client dependencies.
reqwest = { version = "0.11", features = ["json"] }

# WebSocket client dependencies.
tokio = { version = "1.22", features = ["rt", "time", "sync", "macros", "rt-multi-thread"] }
tokio-tungstenite = "0.18"
futures-channel = "0.3"
tokio-stream = "0.1"
tokio-util = "0.7"
40 changes: 8 additions & 32 deletions relay_client/src/errors.rs → relay_client/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,5 @@
pub use tokio_tungstenite::tungstenite::protocol::CloseFrame;

pub type WsError = tokio_tungstenite::tungstenite::Error;
pub type BoxError = Box<dyn std::error::Error + Send + Sync>;

/// Wrapper around the websocket [`CloseFrame`] providing info about the
/// connection closing reason.
#[derive(Debug, Clone)]
pub struct CloseReason(pub Option<CloseFrame<'static>>);

impl std::fmt::Display for CloseReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(frame) = &self.0 {
frame.fmt(f)
} else {
f.write_str("<close frame unavailable>")
}
}
}

/// Errors generated while parsing
/// [`ConnectionOptions`][crate::ConnectionOptions] and creating an HTTP request
/// for the websocket connection.
Expand All @@ -33,7 +15,10 @@ pub enum RequestBuildError {
Url(#[from] url::ParseError),

#[error("Failed to create websocket request: {0}")]
Other(WsError),
WebsocketClient(#[from] crate::websocket::WebsocketClientError),

#[error("Failed to create HTTP request: {0}")]
HttpClient(#[from] crate::http::HttpClientError),
}

/// Possible Relay client errors.
Expand All @@ -42,20 +27,11 @@ pub enum Error {
#[error("Failed to build connection request: {0}")]
RequestBuilder(#[from] RequestBuildError),

#[error("Failed to connect: {0}")]
ConnectionFailed(WsError),

#[error("Connection closed: {0}")]
ConnectionClosed(CloseReason),

#[error("Failed to close connection: {0}")]
ClosingFailed(WsError),

#[error("Not connected")]
NotConnected,
#[error("Websocket client error: {0}")]
WebsocketClient(#[from] crate::websocket::WebsocketClientError),

#[error("Websocket error: {0}")]
Socket(WsError),
#[error("HTTP client error: {0}")]
HttpClient(#[from] crate::http::HttpClientError),

#[error("Internal error: Channel closed")]
ChannelClosed,
Expand Down
Loading

0 comments on commit 21bae4e

Please sign in to comment.