Open rdeago opened 4 years ago
Pinging, in no particular order: @geoperez @mariodivece @JRosanowski @MrDigit @SaricVr @AbeniMatteo @bufferUnderrun @jusovsky @madnik7 @rocketraman
I totally agree with this new direction!
A well-explained issue, I love it.
Totally agree with you. Thanks for making EmbedIO a lot better project.
Coming from a background of programming on other OSes with other languages, I have to admit the whole URL prefix thing was completely unintelligible and seemingly pointless to me when I first encountered it with EmbedIO, C#, and Windows. It makes a lot more sense now with @rdeago 's wonderful history lesson, but one shouldn't generally need history lessons to understand APIs... good change.
Hi @rdeago , been very busy last months and i did not follow all the threads as i used to. Thanks to your ping and thanks for the time you spent describing the issue or working on the project. Same @geoperez and the others guys... 👍
I agree with your report :
The great majority of EmbedIO users seem to just need listening to a port on any address.
All my apps have theirs own listening port and the url routing is done inside modules :
In my opinion, this code removal (abandonned prefix) is more about what do we exactly want from EmbedIO.
I'm a long time IIS user. It grows up from few years to being fat, complicated but lack of cool features like websocket. IIS controls all websites it serve in a way that if things go wrong, all sites are down. I prefer running a separed lightweight process for each sites !
In my searchs, i found EmbedIO three years ago. Well integrated, lightweight, pretty fast and while it support many features i need it remains very simple ! All my webapps have been rewritten to be served by this project. Some for ours internals uses (i work for the city where i live) with approx 3000+ users, 80 sessions live and others for local residents (100000+), peak to 250 sessions.
Funny story for me but not for my coworker, a sysadmin : the first big release in prod based on EmbedIO have down the service. The guilty ? Not EmbedIO but our Apache reverse proxy which burn RAM and PROCESS.
As soon as theses principles (lightweight, simple) will be applied , i will use EmbedIO, one of the critical part of all my apps ! I believe in the choice you make.
I remember the days before prefixes. Me and Stegosaurus used to do socket programming. 😂
Thank you for this awesome project! I used a couple of other projects as well, WiringPi and RaspberryIO, to build a sprinkler controller. I'm using EmbedIO to put together a very simple web API for it that I can control from a Flutter app. I love how quickly you can get something up and running with this - thank you for the detailed plan forward and for working on it!
@bufferUnderrun, @Prehistoricoder, thank you both for your kind words. I'm sure @geoperez and the rest of the team will agree that they mean an awful lot to us.
Not my case but one thing to take into consideration:
HTTP.sys allows you to handle and configure HTTPS and SSL certs externally through netsh http sslcert
and optionally combined with ACME clients.
@AbeniMatteo certificate management is both a thing that we will have to take into consideration, of course, as well as one of the worst aspects of the current version of EmbedIO, precisely because of the limitations it inherits from HTTP.sys via HttpListener
: lack of multi-platform support, no simple (EmbedIO-style) way of using a certificate.
I still have no idea of how to implement HTTPS in a future, HttpListener
-less version of EmbedIO, although probably Kestrel sources are a good place to look at for inspiration. Suggestions are very welcome, by the way.
Adding my vote for this to the pile.. When's 4.0.0 due ? :D
If URL prefixes go away, will there still be a way to only bind to localhost? Binding to localhost, i.e. new WebServer("http://localhost:8080")
conveniently bypasses the Windows Firewall prompt, while new WebServer(8080)
does produce a prompt (a misleading one, because denying it doesn't prevent local connections).
Still voting for this! :)
EmbedIO version 4 will not have URL prefixes: you'll just need a port number and, of course, a certificate if you want to serve HTTPS.
For those with a couple of minutes to spare, here's why we had prefixes to start with, and why they're going to disappear.
I took some liberties with both technical and historical details; feel free to correct me if you think I oversimplified anything. I'm not trying to write the history book on URL prefixes, though, so let's keep it manageable and understandable by non-hard-core-sysadmins such as myself.
TL;DR: Prefixes were a good idea turned sour. They didn't even work as advertised, unless under specific, rather uncommon, conditions.
In the beginning...
Although it feels like early Paleolithic now, there was a time before URL prefixes.
In order to receive any request, an HTTP server had to open a listening TCP socket on a given port. From that moment on, all incoming TCP connections on that port would be directed to that socket and, by consequence, served by that server.
Then all major HTTP servers had their internal mechanisms to "partition" URLs between different applications. This way you could, more or less, install WordPress on
/blog
without affecting your main website in/
, as long as both were served by the same Web server, which took the burden of listening to ports, parsing requests, and sending responses.Putting HTTP on steroids
In the first 2000s, as Internet traffic kept growing at crazy rates, HTTP servers had to handle more and more connections per second.
The problem was, they could optimize their code all they wanted, but they were still bound to make calls to the operating system's networking stack. This implied a lot of switching between "user mode" and "kernel mode" - a costly operation in terms of CPU.
"But wait" - someone at Microsoft probably said - "what if the HTTP protocol was completely implemented in a driver? Less switching, more performance!" Thus,
http.sys
was born: a driver that lifts HTTP protocol management away from programs. It was introduced with Windows Server 2003, as an essential component of IIS 6.0.While they were at it, Microsoft developers also moved the "URL partitioning" part of IIS to the driver. This made a lot of sense for them, because it made IIS even more performant. It made sense for everyone else, too, because, as long as programs used
http.sys
's services (and who wouldn't want to?) they were automatically integrated with each other. You can have an IIS-served website in/
and your C++ application serving URLs starting with/api/
. Yay for Microsoft!Of course the usual criticisms surfaced: programs that depend on
http.sys
will only run in Windows; yet another attempt at vendor lock-down; etc. etc. True, but can you really blame them for scratching their own itch and making their solution available to everyone else, for free?The rise and fall of
HttpListener
Such a fundamental piece of Windows API as the HTTP server API (the user-mode interface to
http.sys
) was too tempting not to write a managed wrapper for it. SoHttpListener
and related types, such asHttpListenerContext
etc., were added to .NET 2.0.Think about it: now you could write your own Web server in C#, or even Visual Basic! How cool is that?
HttpListener
did not come without some drawbacks. It was obviously Windows-only, for starters, but the same went for all of .NET at the time.It was not until 2018, in the age of cross-platform .NET Core, that someone proposed deprecating HttpListener in favor of the fully-managed, cross-platform Kestrel Web server.
Going cross-platform
Well before .NET Core, the Mono project was started by Miguel de Icaza at Ximian to create a fully open-source, cross-platform .NET runtime. Including
HttpListener
of course: they replicatedHttpListener
functionality in fully-managed, cross-platform code.When you configure your EmbedIO
WebServer
withHttpListenerMode.EmbedIO
(the default) you're using Mono's implementation ofHttpListener
.A good idea gone sour
There are two problems with Mono's
HttpListener
.The first problem is that, to achieve the same functionality as Microsoft's
HttpListener
, the Mono team also reimplemented URL prefix management. That's the code that lets you create aHttpListener
forhttp://example.com/
and a different one forhttp://example.com/blog/
, for example. It does so by creating anEndPointListener
for every address / port pair specified in prefixes, which then dispatches each request according to the requested path. These listeners' lifetimes are managed by the staticEndPointManager
class.It would be great, if only it wasn't useless. While Microsoft's code wraps calls to
http.sys
, which keeps track of listeners created by any running program, Mono'sEndPointManager
andEndPointListener
only know aboutHttpListener
instances created in the same program, or worse in the sameAppDomain
! If your Mono program wants to handle requests tohttp://example.com:80/blog/
, but another program (say IIS, or Apache) is already listening for requests tohttp://example.com:80/
, you're simply out of luck. The same program would work in .NET Framework (or .NET Core under Windows), because it would lethttp.sys
manage URL prefixes globally.The second problem with
EndPointManager
andEndPointListener
is that they're pretty buggy. Issues #165, #332, #402, and #459 are but a few examples of how bugs in those two classes can haunt EmbedIO users.Do we really need URL prefixes?
There are two possible use cases for URL prefixes in EmbedIO. Let's examine each of them.
Programs that run under Windows and have to share an address / port pair with IIS or some other program using
http.sys
. I'd bet there are very, very few such programs out there.Programs that create multiple
WebServer
instances and want to share one or more address / port pairs between them. Such programs would be better off usingModuleGroup
s anyway.The great majority of EmbedIO users seem to just need listening to a port on any address. This does not require a dedicated dispatching layer: conflicts, if any, are detected by the OS's networking stack.
If your EmbedIO-based program absolutely needs URL prefixes, please comment on this issue and let's see if we can find a solution.
So what do we do now?
Starting with EmbedIO v4.0, URL prefixes will be abandoned.
WebServerOptionsBase
will have aPort
property and probably anAddress
property; details are not yet finalized at the time of writing.Those of you that are using prefixes of the form
http://+:8080/
will only need to configure the port. Prefixes likehttp://example.com/somewhere/
, i.e. those including a path, will not be supported, just as they are not really supported now unless your program runs under Windows and you explicitly useHttpListenerMode.Microsoft
.On the bright side, nobody's ever going to lose any more sleep on the choice between
http://*:8080/
,http://+:8080/
,http://localhost:8080/
, andhttp://0.0.0.0:8080/
.Obviously,
HttpListenerMode.Microsoft
will still use ~good~ oldHttpListener
, but prefixes will be automatically generated from your Web server's options, thus becoming just an internal implementation detail. In other words, you won't see them.Future-proof
Nobody here wants EmbedIO to follow
HttpListener
on the way to obsolescence. We still don't know what will replaceHttpListener
in future versions of EmbedIO. Suggestions are welcome, by the way.URL prefixes are a feature that binds EmbedIO to
HttpListener
without bringing much benefit. By removing them from our public API, we're preparing for whatever the future may bring.