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

2.0: Drop support for HttpListener, and rewrite the Pode Listener #573

Closed
Badgerati opened this issue May 25, 2020 · 25 comments · Fixed by #579
Closed

2.0: Drop support for HttpListener, and rewrite the Pode Listener #573

Badgerati opened this issue May 25, 2020 · 25 comments · Fixed by #579

Comments

@Badgerati
Copy link
Owner

All right, this one should spark some discussion as it's a big one.

So currently Pode has two ways of running its web server: there's the one that uses HttpListener (the default one), and then there's the Pode one built in PowerShell (-Type Pode on Start-PodeServer) which enables fully xplat SSL and WebSockets - as well as allowing support for hosting Pode via IIS, Azure App Services, and others.

HttpListener is defunct in .NET, so dropping this for just the Pode Listener is ideal - as it supports what HttpListener does, plus allowing more and lets us add more features to the listener as well.

There are however two caveats: performance and complexity.

Under normal circumstances the HttpListener allows for approx. ~180-200req/s - due to being written in .NET and having far better async/event support than PowerShell can deliver.

The Pode Listener can only deliver ~60-70req/s - and this is only because PowerShell's async/event support is dire. Due to this, it also makes the code more complex, and having sleeps everywhere really doesn't help.

If you got this far, then you may have already gathered where this is going - stripping out ONLY the Pode Socket Listener and converting it into C#/.NET Core. This would allow async/events to work as expected, and simplify the code. It might also allow better implementation of KeepAlive on sockets - and allow Client>Server WebSockets rather than just Server>Client.

Now I've already tried this, converting the socket layer to .NET Core, and it works - and boosts the Pode Listener to ~130req/s, and I'm pretty sure I can make that better by using ReceiveAsync on sockets.

But this obviously moves away from Pode being 100% PowerShell, as the Socket Listener will be in C#/.NET Core. So if there's a lot of objection to this, then either we'll keep both HttpListener/Pode or just keep the Pode Listener and keep it as PowerShell.

@RobinBeismann
Copy link
Contributor

RobinBeismann commented May 25, 2020

To me, dropping HttpListener is a sure thing if .NET is dropping support for it on a mid-term.
If converting the Socket Listener to C# is the requirement to keep up the performance is what it takes, then thats the way it shall be.
Powershell has a dependency on .NET anyway, so I think it's not too bad to have the Listener in compiled C# code instead.
Modern applications that use Pode should be based on .NET Core / Powershell >6.0 already in my eyes.

@RobinBeismann
Copy link
Contributor

Just as question on that: By 130req/s you mean, 130req/s per Thread, don't you?

@ArieHein
Copy link
Contributor

ArieHein commented May 25, 2020

You can contemplate that if performance is the main issue what stops converting Pode base to mostly net core with just adding assemblyinfo at the top of the PS scripts that's providing the parameters.
Is Pode meant to be a full scale enterprise web server ? Then I think Auth should be the number one and logging the number two issues dealt first. If on the other hand it is meant for PowerShell users that have interest to assist you in maintaining this project and offering PRs, then its kind of deters them if they lack the understanding of net core.

Part of the reason I started with Pode is that it game me much more choices to customize over Polaris for example without diving too much in net core. While im very comfortable with both and could have gone with a net core based minimal web server framework, the simplicity of PowerShell was the selling point when I introduced it to my team members when we decided what to use as a base for a solution we were building internally.

As HTTPListner is not going forward, i went over to MS GitHub that deals with net core to read the discussions - dotnet/platform-compat#88 and it was quite interesting read that had 3 outcomes that might be of help to you:

Using a minimal Kestrel web server base - https://github.com/davidfowl/BasicKestrel/tree/master/BasicKestrel
which of course still has some ties to aspnet core, which I don't think we can run away from MS adding this dependency upon us.

Alternatively see how its going to work with Blazor.

O R

Base it on another listener that isn't bound to aspnet core and build a PowerShell interface on top of that - https://github.com/featherhttp/tutorial

@Badgerati
Copy link
Owner Author

By 130req/s you mean, 130req/s per Thread, don't you?

@RobinBeismann, That's 130req/s on one thread when I tested, having a second, third, etc ups that but not by much - though to be fair I haven't properly perf tested (#420), and I maxed the CPU from running k6 😂

if performance is the main issue

@ArieHein, performance isn't the main issue; I'd happily switch to Pode's PowerShell listener. The main reason I'm mostly wanting to switch to having just the listener in netcore, is to potentially open up more feature possibilities that the current listener doesn't allow - purely because of PowerShell's lack of async support. Another reason I'm considering moving the listener to netcore is to simplify the code - right now the listener functions but by using runspaces hacks, and a dirty Start-Sleep to stop CPU issues on MacOS.

That being said, performance is still important. If Pode starting becoming sluggish then this would also drive people away. While 60-70req/s is, to be fair, a lot for a PowerShell web server! Adding features could reduce this, and I know of 2 people who have used frameworks like Metro. These frameworks use to try and load a vast amount of css/js and make some pages take a fair few seconds to load. Now yeeees you'd be right in saying "but Pode isn't an enterprise grade web server, so why even use large frontend frameworks?", but it's still nice to let people be able to use what they want 😄 and if I can make Pode go a little faster, then why not!

what stops converting Pode base to mostly net core

I feel a lot more comfortable writing PowerShell than C# 😂, and you hit the nail on the head with "meant for PowerShell users that have interest to assist you in maintaining this project and offering PRs". If I switch a lot of Pode over to C# that would deter a lot of people; this is a reason I want to only convert Pode's listener - it's a section of Pode I imagine very few people would ever delve into, and should be safe to switch (but unlike HttpListener, or any other third-party listener, it's not just a blackbox and can be edited/fixed whenever).

Kestrel, Blazor, and FeatherHttp

I'll bundle these together.

So I did once look into using Kestrel for Pode, to replace HttpListener. However, since Kestrel relies heavily on Delegates and Async patterns, using it PowerShell eventually proved ugly. I did "get it to work", though that was using PSLambda, a couple of sleeps, and it being terribly unreliable. There's also it's big dependency on ASP.NET Core.

Blazor I thought was just front-end C# running on the client side?

Looking a FeatherHttp, I think it may also fall into the same async pattern issues Kestrel did (also looking at the source I think it might depend on ASP.NET Core) - though it does look cool!

One thing I'm mulling over is loosening the "no external dependencies" in Pode - such as having the YamlDotNet dll in Pode. However, when it comes to the listener I'd prefer to keep it within Pode, as we can tailor it specifically to the intricacies of PowerShell.

The one thing HttpListener, and Pode's Listener, does is keep a queue of incoming Requests/Contexts that Pode can just grab in each Runspace (GetContextAsync()). This is the most ideal solution for PowerShell, and for Pode, since it lets use deal with routing and the listener just cares about the Request and the Response.

@ArieHein
Copy link
Contributor

As PS Core / 7.x + is a layer above net core anyway, perhaps kestrel isnt a bad idea as it does give you native support for latest web standards and is faster than httplistener anyway and with latest versions and the next net 5 will probably see more changes and updates to the kestrel listener. But yes, taking out some components that will rarely be changed for the sake of performance isnt a bad idea for the overall adoption of the tool. Should minimize overall dependencies, native yaml support as far as i recall is baked in net or should be so doubt adding external dependencies is required.

@Badgerati
Copy link
Owner Author

While PSCore is above netcore, using Kestrel would mean we'd need to bundle ASP.NET Core's DLLs with Pode's module - making it a lot larger.

If nobody really objects to the above, and are OK with only the listener in Pode being in netcore, then I could look back into #269 again. But this time using Kestrel in netcore, not PowerShell. 🤔

On the other hand, if it's preferred that Pode remains lightweight, then keeping Pode's listener would be more preferable.

On YAML, PowerShell/PowerShell#3607 is still open, so hopefully one day!

@ArieHein
Copy link
Contributor

Hehe you reminded me how long ago it was, look whose the second comment on that thread :)

@jhainau
Copy link
Contributor

jhainau commented May 25, 2020

Would it be feasible to create a standalone httplistener replacement in a separate git repo and make it a prerequisite?

@ArieHein
Copy link
Contributor

Blazor is actually both server side and client side. Only the server side is public atm. Client side is coming "soon".

@Badgerati
Copy link
Owner Author

Hehe you reminded me how long ago it was, look whose the second comment on that thread

@ArieHein, I spotted that when I found the issue again, hahah! 😄

Would it be feasible to create a standalone httplistener

@jhainau, I did actually consider this one when I initially made the all PowerShell listener, but due to it's ugly nature I went against it; this and it's a lot easier to version control the listener if it's part of this repo.

Now with a rewrite of that listener in netcore, it could be feasible to put the listener into a different repo; something that Pode and other people can use - though it depends how much people would want a netcore (non-aspnet-core dependant) socket listener (as it'll be for http, smtp, tcp, etc etc).

For the last part, I would properly tell people to look into Kestrel/etc, because the listener for Pode will be tailored for PowerShell - making it better to keep the listener within this repo.

@ArieHein
Copy link
Contributor

based on the discussion in the thread i mentioned earlier there is a HUGE demand for a listener that will replace HTTP listener and not come bundled with the dependency on asp net.

@ArieHein
Copy link
Contributor

ArieHein commented May 26, 2020

MS just wants to tie it to asp net.

@Badgerati
Copy link
Owner Author

MS just wants to tie it to asp net

So true 😂

there is a HUGE demand for a listener that will replace HTTP listener

It's definitely tempting, though one thing is that it wouldn't replace everything from HttpListener - such as Authentication. It would purely be a listener for Requests/Responses and having a similar Context/Queue pattern to HttpListener.

It is an aspect of Pode that shouldn't change too often, and if people want it then it's definitely an option!

@ArieHein
Copy link
Contributor

i mean kestrel is OSS, dont know if its hard or not to mimic while still decouple it from aspnet

@ArieHein
Copy link
Contributor

There might even be attempts at that by others

@Badgerati
Copy link
Owner Author

I wouldn't want to mimic Kestrel too much, as it is a full-blown web-server - where as this would be just a listener - no auth, routing, etc. Getting a listener to work in netcore without a dependency on aspnet-core is easy.

To be fair trying to mimic Kestrel in just netcore is also possible, but then we might just be creating an ASP2 😂 !

I wouldn't be surprised if others have done it, though it's finding one that's in continuing development/support.

@ArieHein
Copy link
Contributor

I believe AUTH needs to be baked in no matter what.
BTW off topic, slightly, what are your thoughts on the new WinGet system introduced in Build 2020 ?

@Badgerati
Copy link
Owner Author

If we go down the lines of building a generic listener then yeah, we can put auth into it - that I think would make it pretty much like HttpListener 🤔 we should also be able to expose some functions to help with NTLM and Kerberos in Pode!

When it comes to using the listener in Pode though, I'd opt for leaving the auth as middleware with Pode like current, with it being abstracted away from HttpListener so Pode can support other server/hosting types.

On WinGet, I've seen comments about it but haven't looked into it properly myself. Is it basically just Chocolatey but for the Windows Store?

@ArieHein
Copy link
Contributor

seems there's a few vids coming out of build 2020 about authentication libraries that might be of interest - https://www.youtube.com/watch?v=7BvGzFks1pM

@Badgerati
Copy link
Owner Author

Badgerati commented May 28, 2020

out of build 2020 about authentication libraries

I'll have to take a proper look through Build 2020, though most of the ones I could see were for Microsoft Identity/Azure only 🤔 (which could be helpful for Azure auth)


Just to quickly sum up everything above, we've pretty much got 4 solutions (each includes dropping HttpListener):

  1. Keep the PodeListener as PowerShell, and take the hit that it might not be very scalable.
  2. Rewrite the PodeListener in netcore, but keep it in the Pode repo (original scope).
  3. Rewrite the PodeListener in netcore, but make it more general and as it's own repo. Pode will then use it as a dependency.
  4. Screw the rules, use Kestrel (this would include adding all of asp.net core into Pode, so a last resort).

So far 2 and 3 seem to be the best: 2 is the quickest option, 3 will take a bit longer - but if I'm writing a lightweight HttpListener anyway then it could be useful for others.

If I do 3, its first iteration may not include authentication - depending on how long the generalisation takes, and how long it might take to implement them.

I'm thinking the best way to do this is if I just start 3, and we see how it goes. Because doing 3 is basically doing 2 anyway, so it's easy to port back over.

@RobinBeismann
Copy link
Contributor

Does github have a vote option? I'm for option 2.

@Badgerati
Copy link
Owner Author

It doesn't unfortunately, would be really nice if it did! The usual way of doing it in github is to use the reactions.

So if we go down the reaction route, then I'd say vote using the following:

  1. 👍
  2. 😄
  3. 🚀
  4. 👀

(numbers match the options 2 comments up, either react to this comment or the one 2 above)

The other way is possibly SurveyMonkey.

@Juanito99
Copy link

I am more "a user" of Pode and use it to offer REST-APIs for PowerShell scripts.
If the source code stays open I do not mind if Pode will be a mixed with C#.

The reason you provided makes lots of sense to me.

Please go ahead with your work! 👍

@ArieHein
Copy link
Contributor

I would say all of the above ! :)

Basically make the listener layer a module that we can choose in the config.
Internal, simple, good for poc, not for prod, "feature complete", dev frozen unless security issue.
Internal , modern, good for all env, continue dev to get parity with official MS one, including auth etc
external, modern, c# for any usage , not necessarily in coordination with Pode can be used by other projects as a listener on its own.
External, Kestrel based, for the masochists among us ;)
WIN !

OFC its a big chore for but overall its a good investment, i believe, and who knows, i might get my hands covered with mud for a change.

@Badgerati
Copy link
Owner Author

Based on the responses above, I'm going to port Pode's listener to netcore and keep it within the Pode repo.

@ArieHein That's actually not a bad idea! 🤔 while I'm porting the current listener, I'll see if I can make it generic enough so that it could be possible to have something like a "Pode.Kestrel" module; then in Pode have the ability to switch from its listener to another one (a -ListenerType Kestrel perhaps, and you need the "Pode.Kestrel" module). It'd have to be interfaced pretty well, but I do wonder if it's possible!

@Badgerati Badgerati self-assigned this Jun 6, 2020
@Badgerati Badgerati added this to the 2.0.0 milestone Jun 6, 2020
Badgerati added a commit that referenced this issue Jun 6, 2020
Badgerati added a commit that referenced this issue Jun 6, 2020
Badgerati added a commit that referenced this issue Jun 7, 2020
@Badgerati Badgerati changed the title 2.0: Discuss - Drop support for HttpListener, and rewrite the Pode Listener 2.0: Drop support for HttpListener, and rewrite the Pode Listener Jun 8, 2020
Badgerati added a commit that referenced this issue Jun 17, 2020
@Badgerati Badgerati mentioned this issue Nov 14, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants