elastic icon indicating copy to clipboard operation
elastic copied to clipboard

unable to impl Sender due to constraint on elastic::client::private::Sealed

Open softprops opened this issue 7 years ago • 9 comments

Very nice looking crate!

I'm evaluating this as an option to use aws's hosted elastic search. aws requires all requests to be signed which seems most doable with this crate if I implement a Sender that signs requests before sending them. However it seems impl Sender requires implementing a trait that's not visible outside of this crate, elastic::client::private::Sealed.

Do you have another recommendation on how I could accomplish request signing?

softprops avatar Nov 29 '17 06:11 softprops

Hi @softprops thanks for reaching out!

tl;dr We don't accommodate this yet, but I have an idea of how we could reasonably quickly.

This is what the Sender trait is for in the long term, but unfortunately it isn't quite living up to expectations yet, because even if you implement Sender the rest of the client code is expecting there to be only 2 concrete Sender implementations. So most client methods wouldn't be available. That's a terrible experience, so I've added the Sealed trait to it until I get the rest of the client to a point where you can reasonably implement your own Sender.

It looks like right now I haven't really got anything in the API that would make this possible, which is a shame.

For cases like this where you just want a chance to inspect and tweak the raw request before it goes out I'd be on board with adding a function to the client that can be used to inspect and mutate it:

let client = AsyncClientBuilder::new()
    .pre_send_raw(|request| {
        // `request` is `reqwest::async::Request` or `RequestBuilder` here
        // sign the request and either return it or a totally new one
    })

I'd held off on this because it leaks the underlying http library, overlaps with Senders and is a big black box for messing with requests. But since it's just part of the default Senders I think it's reasonable, and quick to implement and convenient when you need it. Maybe I'll put it behind a feature gate.

What do you think?

KodrAus avatar Nov 30 '17 00:11 KodrAus

That would be perfect. The ability to get a handle on the request before it's sent to compute a request signature is precisely what I need.

softprops avatar Nov 30 '17 00:11 softprops

I've got an implementation of this on the vNext branch. You can see it here. This will make it in to the next breaking release, which I'm hoping to push out before Christmas.

KodrAus avatar Dec 11 '17 22:12 KodrAus

Hey @softprops wondering if you ever got aws request signing working using the pre_send_raw method on the client builder? I'm using rusoto for some other stuff and trying to use the existing credential provider and their SignedRequest logic to avoid writing all the signing logic myself but haven't had luck. Would love to see an example if you have one?

Also its been awhile since this was initially opened. Is opening the Sender trait for implementation still the plan @KodrAus ?

Fantastic crate btw. (shout out to softprops as well for all the rust + aws tools)

MrArnoldPalmer avatar Feb 18 '19 06:02 MrArnoldPalmer

Hi @MrArnoldPalmer :wave:

Even though it's been a long time, the state of the world hasn't actually moved that far since then. I've only just managed to ship a prerelease build onto crates.io that includes the pre_send_raw API.

It looks like pre_send_raw might be a little clumsy for that SignedRequest API. I'm guessing you'd need to copy the request data into a SignedRequest structure, sign it, then copy the relevant headers back. It's not ideal.

The Sender trait promises more than it can actually deliver, unfortunately because its concrete implementations are sprinkled around in impl blocks. So a new Sender would be missing most methods. I'd be up for reworking them though so you'd implement either a SyncSender or AsyncSender trait instead of Sender directly, and make our impl blocks that expect a concrete SyncSender or AsyncSender generic over those new traits:

trait SyncSender: Sender {
    ...
}

impl<T: SyncSender> Sender for T {}

trait AsyncSender: Sender {
    ...
}

// NOTE: This won't be coherent, but could work around it
impl<T: AsyncSender> Sender for T {}

Then we basically remove pre_send_raw in favour of writing your own Sender that can encapsulate whatever http stack you want to use.

KodrAus avatar Feb 18 '19 22:02 KodrAus

@KodrAus awesome, looks like it will make this much easier. I'll keep an eye out for this.

I got basic signing functionality working using pre_send_raw, wasn't as hard as I thought but definitely not the prettiest code.

MrArnoldPalmer avatar Feb 19 '19 04:02 MrArnoldPalmer

Was hoping to use this in a WASM project by implementing a sender that uses JS functions but I guess that isn't happening.

ColonelThirtyTwo avatar Aug 27 '19 20:08 ColonelThirtyTwo

Might take a stab at this, because I really want to use this crate in my web app.

My idea was to have the sender specify a type to wrap the results in; ex a plain Result for the sync API and a Future for the async API... unfortunately this needs generic associated types which isn't implemented yet.

ColonelThirtyTwo avatar Oct 02 '19 20:10 ColonelThirtyTwo

So this will require a bit of a refactor around the Client, Sender, and SniffedNodes.

Basically:

  • SyncSender would need to return a result,
  • AsyncSender would need to return a futures::Future,
  • and StdwebSender would need to return a Javascript future object.

So it's not as simple as having one sync sender and one async sender with multiple implementations. The receiving mechanism has to be generic too. In particular the code for refreshing SniffedNodes needs to be pulled into the client's implementation, since it's the only thing that knows when a response is ready.

ColonelThirtyTwo avatar Oct 04 '19 01:10 ColonelThirtyTwo