-
Notifications
You must be signed in to change notification settings - Fork 252
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a transports crate & initial Network abstraction #2
Merged
Merged
Changes from 1 commit
Commits
Show all changes
53 commits
Select commit
Hold shift + click to select a range
83a5c8a
feature: transports crate
prestwich 0203bad
review: address comments
prestwich 7528c5e
refactor: RpcObject trait
prestwich fe6142d
refactor: RpcResult type
prestwich b2e3705
refactor: small code quality
prestwich f1010c6
deps: bump Cargo.toml
prestwich 04f3c49
chore: delete unused src
prestwich 366a5ac
refactor: more stuff
prestwich 4de4a8e
refactor: remove Params type from RpcCall
prestwich 27a6c0f
fix: remove extra to_json_raw_value
prestwich e2703df
refactor: minor legibility
prestwich 53128fd
feat: unwrap variants
prestwich 76bef7f
fix: add debug bounds
prestwich d654407
feature: some cool combinators on rpccall
prestwich 1d691ec
fix: hide __ENFORCE_ZST
prestwich 0073a12
feature: DummyNetwork compile check
prestwich 9d736b5
test: dummynet compile checks
prestwich 268412b
doc: fix comment
prestwich 3cf3f2d
wip: mware and combinator stuff
prestwich 5c53aa8
fuck jsonrpsee
prestwich efb5cdc
feature: blanket
prestwich cf00429
test: http impls transport
prestwich ed0a5e8
feature: send batch request
prestwich 1e9e873
feature: separate rpc type crate
prestwich 21fda6e
feat: RpcObject
prestwich 12f78fa
refactor: transport future aliases
prestwich 2be245a
refactor: transport requires type-erased futures. improved batch ergo
prestwich 38a63ab
wip: some middleware noodling
prestwich dcdb4cf
feature: manual future for json rpc to avoid higher-ranked lifetime
prestwich d45a19d
cleanup: some clippy and stuff
prestwich b8dbb9a
feature: client builder
prestwich 763a5c6
refactor: move is_local to transport
prestwich 9d1f491
chore: clippy
prestwich 0f3fb8f
feature: generic request
prestwich 843009b
feature: allow type-erased rpc client
prestwich 2b83171
chore: cleanup in transports mod
prestwich 562e539
chore: misc cleanup
prestwich ceda9f7
refactor: more crate
prestwich b7a8d1e
chore: clippy cleanup
prestwich 36513b2
fix: lifetimes for rpc calls
prestwich e204269
feature: lifetime on rpccall
prestwich 7a9726e
feature: BoxTransport
prestwich ba8fd02
chore: clippy
prestwich 5cd4826
refactor: cow for jsonrpc params
prestwich 7f8923b
fix: lint
prestwich 047ba1d
refactor: rename to boxed
prestwich c5ce15b
docs and misc convenience
prestwich b7fbff4
refactor: docs and cleanup
prestwich 7173fcc
docs: more of em
prestwich ee69008
refactor: seal transport
prestwich 5770e67
feature: seal transport
prestwich 0c6b2c9
rename middleware to provider
prestwich 8a6a838
chore: remove dead code
prestwich File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
use std::{ | ||
borrow::Borrow, | ||
future::Future, | ||
marker::PhantomData, | ||
pin::Pin, | ||
task::{ready, Context, Poll}, | ||
}; | ||
|
||
use jsonrpsee_types::ErrorObjectOwned; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
use crate::{ | ||
common::{Id, Request, RpcFuture, RpcOutcome}, | ||
utils::{from_json, to_json_raw_value}, | ||
Connection, TransportError, | ||
}; | ||
|
||
pub(crate) enum CallState<B, T, Params> { | ||
Prepared { | ||
connection: B, | ||
method: &'static str, | ||
params: Params, | ||
id: Id<'static>, | ||
_pd: PhantomData<T>, | ||
}, | ||
AwaitingResponse { | ||
fut: RpcFuture, | ||
}, | ||
Complete, | ||
Running, | ||
} | ||
impl<B, T, Params> std::fmt::Debug for CallState<B, T, Params> { | ||
prestwich marked this conversation as resolved.
Show resolved
Hide resolved
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
Self::Prepared { method, id, .. } => f | ||
.debug_struct("Prepared") | ||
.field("method", method) | ||
.field("id", id) | ||
.finish(), | ||
Self::AwaitingResponse { .. } => f.debug_struct("AwaitingResponse").finish(), | ||
Self::Complete => write!(f, "Complete"), | ||
Self::Running => write!(f, "Running"), | ||
} | ||
} | ||
} | ||
|
||
impl<B, T, Params> CallState<B, T, Params> { | ||
pub(crate) fn new( | ||
connection: B, | ||
method: &'static str, | ||
params: Params, | ||
id: Id<'static>, | ||
) -> CallState<B, T, Params> { | ||
Self::Prepared { | ||
connection, | ||
method, | ||
params, | ||
id, | ||
_pd: PhantomData, | ||
} | ||
} | ||
} | ||
|
||
impl<B, T, Params> CallState<B, T, Params> | ||
where | ||
B: Borrow<T> + Unpin, | ||
T: Connection + Unpin, | ||
Params: Serialize + Unpin, | ||
{ | ||
fn poll_prepared(&mut self, cx: &mut Context<'_>) -> Poll<RpcOutcome> { | ||
let this = std::mem::replace(self, CallState::Running); | ||
|
||
match this { | ||
CallState::Prepared { | ||
connection, | ||
method, | ||
params, | ||
id, | ||
.. | ||
} => { | ||
let params = to_json_raw_value(¶ms); | ||
prestwich marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if let Err(err) = params { | ||
*self = CallState::Complete; | ||
return Poll::Ready(Err(err)); | ||
} | ||
let params = params.unwrap(); | ||
gakonst marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let req = Request::owned(id, method, Some(params)); | ||
let fut = connection.borrow().json_rpc_request(&req); | ||
*self = CallState::AwaitingResponse { fut }; | ||
cx.waker().wake_by_ref(); | ||
Poll::Pending | ||
} | ||
_ => panic!(""), | ||
prestwich marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
fn poll_awaiting(&mut self, cx: &mut Context<'_>) -> Poll<RpcOutcome> { | ||
let this = std::mem::replace(self, CallState::Running); | ||
match this { | ||
CallState::AwaitingResponse { mut fut } => { | ||
if let Poll::Ready(val) = fut.as_mut().poll(cx) { | ||
*self = CallState::Complete; | ||
return Poll::Ready(val); | ||
} | ||
*self = CallState::AwaitingResponse { fut }; | ||
Poll::Pending | ||
} | ||
_ => panic!(""), | ||
prestwich marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
} | ||
|
||
impl<B, T, Params> Future for CallState<B, T, Params> | ||
where | ||
B: Borrow<T> + Unpin, | ||
T: Connection + Unpin, | ||
Params: Serialize + Unpin, | ||
{ | ||
type Output = RpcOutcome; | ||
|
||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
let state = self.get_mut(); | ||
match state { | ||
CallState::Prepared { .. } => state.poll_prepared(cx), | ||
CallState::AwaitingResponse { .. } => state.poll_awaiting(cx), | ||
_ => panic!("Polled in bad state"), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct RpcCall<B, T, Params, Resp> { | ||
state: CallState<B, T, Params>, | ||
resp: PhantomData<fn() -> Resp>, | ||
prestwich marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
impl<B, T, Params, Resp> RpcCall<B, T, Params, Resp> { | ||
pub fn new(connection: B, method: &'static str, params: Params, id: Id<'static>) -> Self { | ||
Self { | ||
state: CallState::new(connection, method, params, id), | ||
resp: PhantomData, | ||
} | ||
} | ||
} | ||
|
||
impl<B, T, Params, Resp> Future for RpcCall<B, T, Params, Resp> | ||
where | ||
B: Borrow<T> + Unpin, | ||
T: Connection + Unpin, | ||
Params: Serialize + Unpin, | ||
Resp: for<'de> Deserialize<'de> + Unpin, | ||
{ | ||
type Output = Result<Result<Resp, ErrorObjectOwned>, TransportError>; | ||
|
||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
let state = Pin::new(&mut self.get_mut().state); | ||
let res = ready!(state.poll(cx)); | ||
|
||
match res { | ||
Ok(Ok(val)) => Poll::Ready(from_json(val.get()).map(Result::Ok)), | ||
Ok(Err(err)) => Poll::Ready(Ok(Err(err))), | ||
Err(e) => Poll::Ready(Err(e)), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
use base64::{engine::general_purpose, Engine}; | ||
use serde_json::value::RawValue; | ||
use std::{borrow::Cow, fmt, future::Future, pin::Pin}; | ||
|
||
pub use jsonrpsee_types::{ErrorObject, ErrorResponse, Id, RequestSer as Request, Response}; | ||
|
||
use crate::TransportError; | ||
|
||
#[cfg(target_arch = "wasm32")] | ||
pub(crate) type DynFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>; | ||
#[cfg(not(target_arch = "wasm32"))] | ||
pub(crate) type DynFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>; | ||
|
||
pub type JsonRpcResult<'a> = Result<Cow<'a, RawValue>, ErrorObject<'a>>; | ||
pub type JsonRpcResultOwned = JsonRpcResult<'static>; | ||
|
||
pub type RpcOutcome = Result<JsonRpcResultOwned, TransportError>; | ||
pub type BatchRpcOutcome = Result<Vec<JsonRpcResultOwned>, TransportError>; | ||
|
||
pub type RpcFuture = DynFuture<'static, RpcOutcome>; | ||
pub type BatchRpcFuture = DynFuture<'static, BatchRpcOutcome>; | ||
|
||
/// Basic or bearer authentication in http or websocket transport | ||
/// | ||
/// Use to inject username and password or an auth token into requests | ||
#[derive(Clone, Debug)] | ||
pub enum Authorization { | ||
/// HTTP Basic Auth | ||
Basic(String), | ||
/// Bearer Auth | ||
Bearer(String), | ||
} | ||
|
||
impl Authorization { | ||
/// Make a new basic auth | ||
pub fn basic(username: impl AsRef<str>, password: impl AsRef<str>) -> Self { | ||
let username = username.as_ref(); | ||
let password = password.as_ref(); | ||
let auth_secret = general_purpose::STANDARD.encode(format!("{username}:{password}")); | ||
Self::Basic(auth_secret) | ||
} | ||
|
||
/// Make a new bearer auth | ||
pub fn bearer(token: impl Into<String>) -> Self { | ||
Self::Bearer(token.into()) | ||
} | ||
} | ||
|
||
impl fmt::Display for Authorization { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
Authorization::Basic(auth_secret) => write!(f, "Basic {auth_secret}"), | ||
Authorization::Bearer(token) => write!(f, "Bearer {token}"), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
use thiserror::Error; | ||
|
||
#[derive(Error, Debug)] | ||
pub enum TransportError { | ||
/// SerdeJson (de)ser | ||
#[error("{err}")] | ||
gakonst marked this conversation as resolved.
Show resolved
Hide resolved
|
||
SerdeJson { | ||
err: serde_json::Error, | ||
text: String, | ||
}, | ||
|
||
/// Http transport | ||
#[error(transparent)] | ||
Reqwest(#[from] reqwest::Error), | ||
} | ||
|
||
impl TransportError { | ||
pub fn ser_err(err: serde_json::Error) -> Self { | ||
prestwich marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Self::SerdeJson { | ||
err, | ||
text: "".to_string(), | ||
} | ||
} | ||
|
||
pub fn deser_err(err: serde_json::Error, text: impl AsRef<str>) -> Self { | ||
prestwich marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Self::SerdeJson { | ||
err, | ||
text: text.as_ref().to_string(), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,25 @@ | ||
pub fn add(left: usize, right: usize) -> usize { | ||
left + right | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn it_works() { | ||
let result = add(2, 2); | ||
assert_eq!(result, 4); | ||
} | ||
} | ||
#![warn( | ||
missing_debug_implementations, | ||
// missing_docs, | ||
unreachable_pub, | ||
// unused_crate_dependencies | ||
)] | ||
#![deny(unused_must_use, rust_2018_idioms)] | ||
#![doc(test( | ||
no_crate_inject, | ||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) | ||
))] | ||
|
||
pub mod common; | ||
pub(crate) mod utils; | ||
|
||
mod error; | ||
pub use error::TransportError; | ||
|
||
mod call; | ||
|
||
mod transport; | ||
pub use transport::{Connection, PubSubConnection}; | ||
|
||
pub mod transports; | ||
pub use transports::Http; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
use futures_channel::mpsc::UnboundedReceiver; | ||
use serde::{Deserialize, Serialize}; | ||
use serde_json::value::RawValue; | ||
|
||
use std::{borrow::Cow, fmt::Debug}; | ||
|
||
use crate::{call::RpcCall, common::*, TransportError}; | ||
|
||
pub trait Connection: Debug + Send + Sync { | ||
fn is_local(&self) -> bool; | ||
|
||
fn increment_id(&self) -> u64; | ||
|
||
fn next_id(&self) -> Id<'static> { | ||
Id::Number(self.increment_id()) | ||
} | ||
|
||
fn json_rpc_request(&self, req: &Request<'_>) -> RpcFuture; | ||
|
||
fn batch_request(&self, reqs: &[Request<'_>]) -> BatchRpcFuture; | ||
|
||
fn request<Params, Resp>( | ||
&self, | ||
method: &'static str, | ||
params: Params, | ||
) -> RpcCall<&Self, Self, Params, Resp> | ||
where | ||
Self: Sized, | ||
Params: Serialize, | ||
Resp: for<'de> Deserialize<'de>, | ||
{ | ||
RpcCall::new(self, method, params, self.next_id()) | ||
} | ||
} | ||
|
||
pub trait PubSubConnection: Connection { | ||
#[doc(hidden)] | ||
fn uninstall_listener(&self, id: [u8; 32]) -> Result<(), TransportError>; | ||
|
||
#[doc(hidden)] | ||
fn install_listener( | ||
&self, | ||
id: [u8; 32], | ||
) -> Result<UnboundedReceiver<Cow<'_, RawValue>>, TransportError>; | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use crate::{Connection, PubSubConnection}; | ||
|
||
fn __compile_check() -> Box<dyn Connection> { | ||
todo!() | ||
} | ||
fn __compile_check_pubsub() -> Box<dyn PubSubConnection> { | ||
todo!() | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels like we'll need to fill this thing with docs given how much thinking went into the type separations
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I'll work on it a bit. but right now i'm trying to iterate without docs until it gets really firm