Skip to content
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

API design to get hold of a Tokio handle within a handler #78

Closed
ChristophWurst opened this issue Dec 30, 2017 · 7 comments
Closed

API design to get hold of a Tokio handle within a handler #78

ChristophWurst opened this issue Dec 30, 2017 · 7 comments
Assignees
Milestone

Comments

@ChristophWurst
Copy link
Contributor

As discussed on Gitter with @bradleybeddoes, I'm missing the possibility to send new HTTP requests inside Gotham handlers because currently it's not possible to get a reference to the underlying Tokio event loop.

@bradleybeddoes pointed me to

fn call(&self, req: Self::Request) -> Self::Future {
where other framework state variables are initialized. Unfortunately, I don't see a way of accessing the tokio handle in that function. As a matter of fact the whole Gotham codebase is pretty much unaware of the whole Tokio stuff because the way Gotham apps seem to be started is that the handler is passed to Hyper's server API. Hyper will either create the Tokio core internally, or let you use an existing one (since #66).

An alternative to having Gotham put the handle reference into the state object would be to use a middleware for that: https://github.com/ChristophWurst/gotham-middleware-tokio. You can find an example usage here: https://github.com/ChristophWurst/gotham-middleware-tokio/blob/master/examples/hyper_client.rs#L22

There seem to be a few problems though. I don't know whether we should use a Handle, a Remote or let the developer choose. Handle cannot be shared across threads. However, Hyper expects a Handle rather than a Remote for its client, making it a bit cumbersome to use. The remote object does have method to get a handle, although it's not guaranteed to always get one.

This is somewhat related to hyperium/hyper#1002

@bradleybeddoes @smangelsdorf thoughts on how to integrate this in Gotham?

@bradleybeddoes
Copy link
Contributor

I'm not sure what @smangelsdorf had in mind but I was thinking of something along the lines of passing a handle reference to

pub fn new(t: T) -> NewHandlerService<T> {
and then being able to use that within
fn call(&self, req: Self::Request) -> Self::Future {
to push it into state.

I may be missing some importance nuance here as I've only very briefly considered this, happy to keep kicking this around if I've overlooked something important.

If we do change

pub fn new(t: T) -> NewHandlerService<T> {
then #70 will need to consider the impact.

@rcaught
Copy link

rcaught commented Jan 4, 2018

Related: #69 will need access to the event loop as well.

@smangelsdorf
Copy link
Contributor

I don't know whether we should use a Handle, a Remote or let the developer choose. Handle cannot be shared across threads. However, Hyper expects a Handle rather than a Remote for its client, making it a bit cumbersome to use.

Tokio was discussing a change which merges both these types into a Handle which is also usable in the way Remote is currently used. If that's still their plan (which seems likely), sticking with what Hyper wants will ensure we automatically get the benefit of this refactor when it happens.

@smangelsdorf
Copy link
Contributor

@bradleybeddoes:

[…] passing a handle reference to

pub fn new(t: T) -> NewHandlerService<T> {
and then being able to use that within
fn call(&self, req: Self::Request) -> Self::Future {
to push it into state

One complicating factor here is that we're creating a NewHandlerService and then cloning it for each connection:

let service = NewHandlerService::new(new_handler);
info!(
target: "gotham::start",
" Gotham listening on http://{} with {} threads",
addr,
threads,
);
for _ in 0..threads - 1 {
let listener = listener.try_clone().expect("unable to clone TCP listener");
let protocol = protocol.clone();
let service = service.clone();
thread::spawn(move || serve(listener, &addr, &protocol, &service));
}

The structure here might have to change a bit, to have NewHandlerService::new take an Arc<T> and a Handle. The local serve function could then be changed to take the Arc<T> and have it create the NewHandlerService.

With this small refactor I think your suggestion will work well. 👍

@smangelsdorf
Copy link
Contributor

then #70 will need to consider the impact.

I can't see where there would be any impact on #70. Did you have something specific in mind?

@bradleybeddoes
Copy link
Contributor

I can't see where there would be any impact on #70. Did you have something specific in mind?

Apologies I mis-spoke on this one, please ignore that comment.

@bradleybeddoes bradleybeddoes added this to the 0.2 milestone Jan 10, 2018
smangelsdorf added a commit that referenced this issue Jan 11, 2018
This lays the groundwork for addressing #78 - exposing the `Handle` to
Gotham applications.
@bradleybeddoes
Copy link
Contributor

🎉 😂 💫 💌 📣

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants