-
Notifications
You must be signed in to change notification settings - Fork 273
Client http transport #420
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
Conversation
I've changed my approach here: initially I copied @tomusdrw's approach of creating a pair of channels for the jsonrpc/core-client/src/transports/http.rs Lines 17 to 33 in 7a1c1f4
jsonrpc/core-client/src/transports/mod.rs Lines 10 to 40 in 7a1c1f4
However this ran into problems where HTTP errors would not be returned to the caller, because there is no way to correlate the error response with the sender in So I've created a "standalone" HTTP transport, which can handle the errors properly since it is request/response instead of duplex. I've extracted the former Still a bit more work to do on this - but would be good to have some feedback to see whether this is the right direction. |
What happens currently? Is the error at least logged? I see it's logged in the ws client which is suboptimal. |
I think it would only be logged if you attach a logger to the client future error e.g. https://github.com/paritytech/substrate/pull/2255/files#diff-74c6a3e10a271935aa30a9981463e681R76 |
* Run rustfmt. * Make RpcMessage private. * Move ws/client to core-client.
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.
Looks good after the first glance, couple of small nits.
core-client/src/lib.rs
Outdated
channel: Some(channel), | ||
outgoing: VecDeque::new(), | ||
} | ||
impl Into<RpcChannel> for mpsc::Sender<RpcMessage> { |
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.
I think we should rather implement From
:
Library authors should not directly implement this[Into] trait, but should prefer implementing the From trait, which offers greater flexibility and provides an equivalent Into implementation for free, thanks to a blanket implementation in the standard library.
core-client/src/transports/duplex.rs
Outdated
} | ||
|
||
/// Creates a new `Duplex`, along with a channel to communicate | ||
pub fn with_channel(sink: TSink, stream: TStream) -> (Self, RpcChannel) { |
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.
The method is a bit confusing, but seeing with_channel
I think I was expecting the channel to be passed as parameter instead, I'd swap the names.
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.
I've pulled this out to a public function duplex
. Not sure about the name but perhaps more idiomatic for creating the (Duplex, RpcChannel)
tuple.
if self.channel.is_none() { | ||
break; | ||
} | ||
let msg = match self.channel.as_mut().expect("channel is some; qed").poll() { |
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.
You could match on self.channel.as_mut()
to avoid expect
break; | ||
} | ||
Ok(Async::NotReady) => break, | ||
Err(()) => continue, |
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.
That will lead to an endless loop, why do we continue
if the Sender
is dropped? We should rather resolve with an error after processing the outgoing
queue?
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.
Seems the futures impl of mpsc returns Ready(None)
when the channel is closed - which is handled above.
And indeed that is documented as the expected behaviour for Stream
, and for future versions.
// Handle outgoing rpc requests. | ||
loop { | ||
match self.outgoing.pop_front() { | ||
Some(request) => match self.sink.start_send(request)? { |
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.
Wouldn't it be easier to use send_all
instead?
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.
Task in #432
Some(tx) => tx | ||
.send(result) | ||
.map_err(|_| RpcError::Other(format_err!("oneshot channel closed")))?, | ||
None => Err(RpcError::UnknownId)?, |
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.
I don't think it should be an error - this means the server can terminate the entire stream by sending a malformed response. I'd rather handle that gracefuly with a warning and continue
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.
Task in #432
debug!("client finished"); | ||
Ok(Async::Ready(())) | ||
} else { | ||
Ok(Async::NotReady) |
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.
I'm not sure if this is correct, are we sure that in every case where we go into this branch we poll
something upstream? Otherwise the task is not going to be woken up, I was looking at different cases, but it seems correct, so just leaving a notice here :)
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.
Task in #432
core-client/src/transports/http.rs
Outdated
http::header::HeaderValue::from_static("application/json"), | ||
) | ||
.body(request.into()) | ||
.unwrap(); |
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.
could do with a proof
.into_iter() | ||
.map(|output| { | ||
let id = output.id().clone(); | ||
let value: Result<Value, Error> = output.into(); |
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.
This conversion here might be a bit wasteful, can't we have some more strict type than Value
?
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.
Wasteful in what way? The output
contains the json Value
and it is just transformed to a Result
jsonrpc/core/src/types/response.rs
Line 78 in 31ec6d6
Output::Success(s) => Ok(s.result), |
I've opened an issue to address some of the points with |
Rel: paritytech/substrate#2255.
Still todo:
join
or wrap in a struct which implsFuture