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

Plugin hooks for WebSockets #1849

Closed
wants to merge 9 commits into from
Closed

Plugin hooks for WebSockets #1849

wants to merge 9 commits into from

Conversation

Jesus
Copy link
Contributor

@Jesus Jesus commented Jul 16, 2019

This is a proof of concept and it's not ready for merge at all, I'm opening this PR just to gauge interest.

What I'm trying here is to design a minimal interface that would allow an external plugin to implement WebSocket support. This has some advantages for Puma compared to the previous proposal:

  1. Less maintenance burden for Puma maintainers.
  2. Independent release cycle, the new code from the plugin won't hurt Puma if it has bugs.
  3. Allow users to opt-in for this, therefore websocket-driver won't be a dependency.
  4. Other features can fall down from plugins without requiring any update in Puma. Not that I can foresee any atm but who knows... (maybe SSE?).

But also some minor disadvantages:

  1. Changes in the hooks interface between the plugin & Puma may be troublesome. However it should be the plugin's responsibility to specify the correct Puma version it depends on. Ideally we won't need to make changes.
  2. Integration tests will be more difficult, but again this'll often be the plugins problem.

The current prototype as it stands has been able to run a basic chat application and this ActionCable experiment, using this puma-websockets plugin.

I understand this PR as it is doesn't cover many edge cases and it lacks tests, but I'd be happy to work further if you think this approach is worth more time.

@nateberkopec
Copy link
Member

How would this impact #1809?

@Jesus
Copy link
Contributor Author

Jesus commented Jul 16, 2019 via email

@Jesus
Copy link
Contributor Author

Jesus commented Jul 24, 2019

Hi @evanphx,

if we wanted to move forward having server-level support for WebSockets, would you prefer the approach from #1809 or something like what's in this PR?

I understand reviewing this PR is quite demanding but, even without reading it thoroughly, it would help me a ton if I can just discard one of the two approaches.

Thanks for your time!

@@ -652,6 +672,10 @@ def handle_request(req, lines)
#
after_reply = env[RACK_AFTER_REPLY] = []

unless @on_before_rack.nil?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should just be if @on_before_rack.

lib/puma/stream_client.rb Show resolved Hide resolved
@nateberkopec
Copy link
Member

Hi @Jesus. I'd like to help you get these webhook PRs over the line. Can you email me at the address on my GH profile?

@nateberkopec
Copy link
Member

@Jesus and I spoke about this offline. We agreed that I would come in and provide some feedback here on the direction, and then close this PR.

Jesus said he doesn't have quite as much time to work on this as he used to. I think if someone wanted to take up the issue of websocket support in Puma, taking over this PR would be a good place to start.

## Per request hooks

`#on_before_rack(env)` will be called right before the Rack application is
invoked. The called hook may modify `env` just like any Rack middleware.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way you've phrased this: "may modify X just like any Rack middleware" makes me wonder if we should just expand this to a "puma middleware" concept, where puma middleware are otherwise like Rack middleware but may also return an object that responds to stream? rather than a rack response. That would allow you to "stack" multiple on/before hooks, in order.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an interesting idea, I'd like to think more about its implications but right now I believe it makes sense.

to `#stream?` with a truthy value. Check out `lib/puma/stream_client.rb` to
know more about this interface.

If more than one plugin arises interest in taking over, an exception will
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proposed "puma middleware" idea would allow us to handle this case more gracefully.


if stream_client && stream_client.stream?
if is_async
raise "Only one #on_after_rack hook should take over"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can avoid this by just giving the user some notion of an "insertion order" - insert this plugin at this point the stack, then this plugin, etc, and the first plugin that is_async gets to take over.

@@ -0,0 +1,72 @@
module Puma
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this part -> onward would be in a plugin gem, not in Puma core.

@nateberkopec
Copy link
Member

LGTM @Jesus. I think the interface is solid and easy to understand. A few comments about the concept of before/after handlers, but otherwise I like the direction a lot!

@Jesus
Copy link
Contributor Author

Jesus commented Oct 2, 2019

Thanks for the feedback Nate, hopefully I can resurrect this soon. (Or maybe someone else will...)

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

Successfully merging this pull request may close these issues.

None yet

3 participants