diff --git a/Playground/Program.cs b/Playground/Program.cs index 8333f9870..96fe539a8 100644 --- a/Playground/Program.cs +++ b/Playground/Program.cs @@ -1,9 +1,13 @@ using GenHTTP.Engine.Internal; -using GenHTTP.Modules.IO; +using GenHTTP.Modules.Practices; -var app = Content.From(Resource.FromString("Hello World!")); +using GenHTTP.Playground.Samples; -await Host.Create() - .Handler(app) - .RunAsync(); +var sample = CustomFrameworkSample.Create(); + +return await Host.Create() + .Handler(sample) + .Development() + .Defaults() + .RunAsync(); diff --git a/Playground/README.md b/Playground/README.md new file mode 100644 index 000000000..2dfb5673c --- /dev/null +++ b/Playground/README.md @@ -0,0 +1,14 @@ +# GenHTTP Playground + +The playground provides code samples for quick reference and allows developers to +quickly validate things. + +| Sample | Description | +|----------------------------------------------------------------------|----------------------------------------------------------------------------------| +| [Layouting](./Samples/LayoutingSample.cs) | Allows an app to use multiple handlers by adding routing. | +| [Webservices](./Samples/WebserviceSample.cs) | Implements a REST webservice via a handler class. | +| [Functional](./Samples/FunctionalSample.cs) | Uses delegates to implement a web service (similar to ASP.NET Core Minimal API). | +| [Controllers](./Samples/ControllerSample.cs) | Uses controllers to implement a web service (similar to ASP.NET Core MVC). | +| [Static Files](./Samples/StaticFileSample.cs) | Serves static files from a directory. | +| [Static Websites](./Samples/StaticWebsiteSample.cs) | Hosts a static website (with `index.html` support). | +| [Single Page Applications](./Samples/SinglePageApplicationSample.cs) | Hosts a SPA such as a React or Angular application. | diff --git a/Playground/Samples/ControllerSample.cs b/Playground/Samples/ControllerSample.cs new file mode 100644 index 000000000..e24a2ef25 --- /dev/null +++ b/Playground/Samples/ControllerSample.cs @@ -0,0 +1,62 @@ +using GenHTTP.Api.Content; + +using GenHTTP.Modules.Controllers; +using GenHTTP.Modules.Layouting; +using GenHTTP.Modules.Reflection; + +namespace GenHTTP.Playground.Samples; + +public static class ControllerSample +{ + + public static IHandlerBuilder Create() + { + /* + * + * Shows how to declare and register a controller that will be invoked + * to handle incoming HTTP requests. + * + * See https://genhttp.org/documentation/content/frameworks/controllers/ + * + */ + + return Layout.Create() + .AddController("books"); + } + + public record Book(int Id, string Title); + + public class BookController + { + private static readonly List Books = + [ + new(1, "Lord of the Rings") + ]; + + // GET http://localhost:8080/books/ + [ControllerAction] + public List Index() => Books; + + // PUT http://localhost:8080/books/create with JSON/XML/... payload + [ControllerAction(Method.Put)] + public Book Create(Book book) + { + var toAdd = book with + { + Id = Books.Max(b => b.Id) + 1 + }; + + Books.Add(toAdd); + return toAdd; + } + + // DELETE http://localhost:8080/books/delete/1 + [ControllerAction(Method.Delete)] + public void Delete([FromPath] int id) + { + Books.RemoveAll(b => b.Id == id); + } + + } + +} diff --git a/Playground/Samples/CustomFrameworkSample.cs b/Playground/Samples/CustomFrameworkSample.cs new file mode 100644 index 000000000..992af6c1e --- /dev/null +++ b/Playground/Samples/CustomFrameworkSample.cs @@ -0,0 +1,81 @@ +using GenHTTP.Api.Content; +using GenHTTP.Api.Infrastructure; +using GenHTTP.Api.Protocol; + +using GenHTTP.Modules.ApiBrowsing; +using GenHTTP.Modules.Conversion; +using GenHTTP.Modules.Functional.Provider; +using GenHTTP.Modules.Layouting; +using GenHTTP.Modules.OpenApi; +using GenHTTP.Modules.Reflection; +using GenHTTP.Modules.Reflection.Operations; + +namespace GenHTTP.Playground.Samples; + +public static class CustomFrameworkSample +{ + + public static IHandlerBuilder Create() + { + /* + * + * Shows how to implement a custom framework. + * + * See https://genhttp.org/documentation/content/frameworks/custom/ + * + */ + + return Layout.Create() + .Add(new CustomFrameworkHandler()) + .AddOpenApi() + .AddScalar(); + } + + public class CustomFrameworkHandler : IHandler, IServiceMethodProvider + { + private MethodCollection? _methods; + + public MethodCollection Methods => _methods ?? throw new InvalidOperationException("Handler is not prepared yet"); + + public async ValueTask PrepareAsync(IServer server) + { + var list = new List(); + + // specify the supported methods of the operation we are exposing + var supportedMethods = new MethodConfiguration([RequestMethod.Get]); + + // the actual piece of code to be executed - either a method info or a delegate + var methodInfo = GetType().GetMethod("ExposedMethod")!; + + // auto enables code generation, otherwise reflection only + var executionSettings = new ExecutionSettings(ExecutionMode.Auto); + + // configures the behavior for serialization, injection and formatting + var registry = new MethodRegistry( + Serialization.Default().Build(), + Injection.Default().Build(), + Formatting.Default().Build() + ); + + // build the operation we would like to provide + var operation = OperationBuilder.Create(server, ":id", methodInfo, null, executionSettings, supportedMethods, registry); + + // create a method handler from the operation which will serve it as an HTTP endpoint + // this handler requires an instance provider which tells the framework on which + // object to invoke the given method info or delegate + list.Add(new MethodHandler(operation, (_) => new(this), registry)); + + // build a method collection handler from all collected operations + // this handler is responsible for routing + _methods = new MethodCollection(list); + + await _methods.PrepareAsync(server); + } + + public ValueTask HandleAsync(IRequest request) => Methods.HandleAsync(request); + + public string ExposedMethod(int id) => id.ToString(); + + } + +} diff --git a/Playground/Samples/FunctionalSample.cs b/Playground/Samples/FunctionalSample.cs new file mode 100644 index 000000000..f4f6463c4 --- /dev/null +++ b/Playground/Samples/FunctionalSample.cs @@ -0,0 +1,47 @@ +using GenHTTP.Api.Content; + +using GenHTTP.Modules.Functional; +using GenHTTP.Modules.Layouting; + +namespace GenHTTP.Playground.Samples; + +public static class FunctionalSample +{ + + public static IHandlerBuilder Create() + { + /* + * + * Shows how to use delegates that will be invoked + * to handle incoming HTTP requests. + * + * See https://genhttp.org/documentation/content/frameworks/functional/ + * + */ + + var books = new List() + { + new(1, "Lord of the Rings") + }; + + var service = Inline.Create() + .Get(() => books) // GET http://localhost:8080/books/ + .Put((Book book) => // PUT http://localhost:8080/books/ with JSON/XML/... payload + { + var toAdd = book with + { + Id = books.Max(b => b.Id) + 1 + }; + + books.Add(toAdd); + return toAdd; + }) + .Delete(":id", (int id) => books.RemoveAll(b => b.Id == id)); // DELETE http://localhost:8080/books/1 + + return Layout.Create() + .Add("books", service); + } + + public record Book(int Id, string Title); + +} diff --git a/Playground/Samples/LayoutingSample.cs b/Playground/Samples/LayoutingSample.cs new file mode 100644 index 000000000..dd03f9c3e --- /dev/null +++ b/Playground/Samples/LayoutingSample.cs @@ -0,0 +1,38 @@ +using GenHTTP.Api.Content; + +using GenHTTP.Modules.Files; +using GenHTTP.Modules.Functional; +using GenHTTP.Modules.Layouting; + +namespace GenHTTP.Playground.Samples; + +public static class LayoutingSample +{ + + public static IHandlerBuilder Create() + { + /* + * + * Layouts allow you to structure your web application and divide it into + * different logical parts. The layout will route incoming requests to the designated + * handler. Layouts can be nested as needed. + * + * See https://genhttp.org/documentation/content/handlers/layouting/ + * + */ + + // serve static files from the current directory + var assets = Assets.From("./"); + + // define a simple REST API + var api = Inline.Create() + .Get(() => "Hello World!"); + + // assemble our application from the parts above and + // register them at the given URLs + return Layout.Create() + .Add("assets", assets) // e.g. GET http://localhost:8080/assets/GenHTTP.Playground.xml + .Add("api", api); // e.g. GET http://localhost:8080/api/ + } + +} diff --git a/Playground/Samples/SinglePageApplicationSample.cs b/Playground/Samples/SinglePageApplicationSample.cs new file mode 100644 index 000000000..e0163ca48 --- /dev/null +++ b/Playground/Samples/SinglePageApplicationSample.cs @@ -0,0 +1,27 @@ +using GenHTTP.Api.Content; + +using GenHTTP.Modules.IO; +using GenHTTP.Modules.SinglePageApplications; + +namespace GenHTTP.Playground.Samples; + +public static class SinglePageApplicationSample +{ + + public static IHandlerBuilder Create() + { + /* + * + * Serves a single page application (SPA) from a directory (or resource tree in general). + * + * See https://genhttp.org/documentation/content/frameworks/single-page-applications/ + * + */ + + var files = ResourceTree.FromDirectory("/var/app"); + + return SinglePageApplication.From(files); + } + +} + diff --git a/Playground/Samples/StaticFileSample.cs b/Playground/Samples/StaticFileSample.cs new file mode 100644 index 000000000..2e30d43e6 --- /dev/null +++ b/Playground/Samples/StaticFileSample.cs @@ -0,0 +1,24 @@ +using GenHTTP.Api.Content; + +using GenHTTP.Modules.Files; + +namespace GenHTTP.Playground.Samples; + +public static class StaticFileSample +{ + + public static IHandlerBuilder Create() + { + /* + * + * Serves files from a given directory or resource tree. + * + * See https://genhttp.org/documentation/content/handlers/static-content/ + * + */ + + // e.g. GET http://localhost:8080/assets/GenHTTP.Playground.xml + return Assets.From("./"); + } + +} diff --git a/Playground/Samples/StaticWebsiteSample.cs b/Playground/Samples/StaticWebsiteSample.cs new file mode 100644 index 000000000..7b2f88a5a --- /dev/null +++ b/Playground/Samples/StaticWebsiteSample.cs @@ -0,0 +1,27 @@ +using GenHTTP.Api.Content; + +using GenHTTP.Modules.IO; +using GenHTTP.Modules.StaticWebsites; + +namespace GenHTTP.Playground.Samples; + +public static class StaticWebsiteSample +{ + + public static IHandlerBuilder Create() + { + /* + * + * Serves a static website from a directory (or resource tree in general). + * + * See https://genhttp.org/documentation/content/frameworks/static-websites/ + * + */ + + var files = ResourceTree.FromDirectory("/var/www"); + + return StaticWebsite.From(files); + } + +} + diff --git a/Playground/Samples/WebserviceSample.cs b/Playground/Samples/WebserviceSample.cs new file mode 100644 index 000000000..6a672d5d8 --- /dev/null +++ b/Playground/Samples/WebserviceSample.cs @@ -0,0 +1,63 @@ +using GenHTTP.Api.Content; + +using GenHTTP.Modules.Controllers; +using GenHTTP.Modules.Layouting; +using GenHTTP.Modules.Reflection; +using GenHTTP.Modules.Webservices; + +namespace GenHTTP.Playground.Samples; + +public static class WebserviceSample +{ + + public static IHandlerBuilder Create() + { + /* + * + * Shows how to declare and register a class that will be invoked + * to handle incoming HTTP requests. + * + * See https://genhttp.org/documentation/content/frameworks/webservices/ + * + */ + + return Layout.Create() + .AddService("books"); + } + + public record Book(int Id, string Title); + + public class BookService + { + private static readonly List Books = + [ + new(1, "Lord of the Rings") + ]; + + // GET http://localhost:8080/books/ + [ResourceMethod] + public List List() => Books; + + // PUT http://localhost:8080/books/ with JSON/XML/... payload + [ResourceMethod(Method.Put)] + public Book Create(Book book) + { + var toAdd = book with + { + Id = Books.Max(b => b.Id) + 1 + }; + + Books.Add(toAdd); + return toAdd; + } + + // DELETE http://localhost:8080/books/1 + [ResourceMethod(Method.Delete, ":id")] + public void Delete([FromPath] int id) + { + Books.RemoveAll(b => b.Id == id); + } + + } + +} diff --git a/README.md b/README.md index 258dd6ce6..4bf7ed65d 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,52 @@ # GenHTTP Webserver GenHTTP is a lightweight, modular web server written in pure C# with a strong focus on developer experience. The main -purpose of this project is to quickly create web services written in .NET 8 / 9 / 10, allowing developers to concentrate on +purpose of this project is to quickly create web services written in .NET 10 / 11, allowing developers to concentrate on the functionality rather than on messing around with configuration files or complex concepts. -[![View - Documentation](https://img.shields.io/badge/view-Documentation-AB54FF)](https://genhttp.org/documentation/) [![nuget Package](https://img.shields.io/nuget/v/GenHTTP.Core.svg)](https://www.nuget.org/packages/GenHTTP.Core/) [![HTTP Arena](https://img.shields.io/endpoint?url=https://www.http-arena.com/badge/genhttp/h1.json)](https://www.http-arena.com/#type=emerging,flagship&tuned=1) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=GenHTTP&metric=coverage)](https://sonarcloud.io/dashboard?id=GenHTTP) [![Discord](https://discordapp.com/api/guilds/1177529388229734410/widget.png?style=shield)](https://discord.gg/PRkwKrnrB4) - -## Features - -- Setup new webservices in a couple of minutes using [project templates](https://genhttp.org/documentation/content/templates/) -- Supports [current standards](https://genhttp.org/features/) such as Open API, Websockets, Server Sent Events or JWT authentication -- Embed web services into a new or already existing console, service, WPF, WinForms, WinUI, MAUI, Avalonia or Uno application -- Projects are fully described in code - no configuration files needed, no magical behavior you need to learn -- Optionally supports [Kestrel](https://genhttp.org/documentation/server/engines/) as an underlying HTTP engine (enables HTTP/2 and HTTP/3 via QUIC) -- [Optimized](https://genhttp.org/features/) out of the box, small memory and storage [footprint](https://genhttp.org/features/#footprint) -- Carefully hand-crafted code, with stable releases and long-term [support options](https://genhttp.org/support/) -- Grade A+ security level according to SSL Labs, hardened against typical attack vectors +[![View - Documentation](https://img.shields.io/badge/view-Documentation-AB54FF)](https://genhttp.org/documentation/) [![nuget Package](https://img.shields.io/nuget/v/GenHTTP.Full.svg)](https://www.nuget.org/packages/GenHTTP.Full/) [![HTTP Arena](https://img.shields.io/endpoint?url=https://www.http-arena.com/badge/genhttp/h1.json)](https://www.http-arena.com/#type=emerging,flagship&tuned=0) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=GenHTTP&metric=coverage)](https://sonarcloud.io/dashboard?id=GenHTTP) [![Discord](https://discordapp.com/api/guilds/1177529388229734410/widget.png?style=shield)](https://discord.gg/PRkwKrnrB4) ## Getting Started -This section shows how to create a new project from scratch using project templates and how to extend your existing -application by embedding the GenHTTP engine. - -> [!NOTE] -> This is a brief overview to get you running. You might want to have a look at -> the [tutorials](https://genhttp.org/documentation/tutorials/) for detailed step-by-step guides. - -### New Project - -Project templates can be used to create apps for typical use cases with little effort. After installing -the [.NET SDK](https://dotnet.microsoft.com/en-us/download) and the templates via `dotnet new -i GenHTTP.Templates` in -the terminal, the templates are available via the console or directly in Visual Studio: - - +To host a GenHTTP server instance in an existing or new .NET project, add a nuget reference to `GenHTTP.Full` to your +project and spin off a new host: -To create a project by using the terminal, create a new folder for your app and use one of the following commands: - -| Template | Command | Documentation | -|-------------------------------|---------------------------------------------|------------------------------------------------------------------------------------------------------------------| -| REST Webservice | `dotnet new genhttp-webservice` | [Webservices](https://genhttp.org/documentation/content/frameworks/webservices/) | -| REST Webservice (single file) | `dotnet new genhttp-webservice-minimal` | [Functional Handlers](https://genhttp.org/documentation/content/frameworks/functional/) | -| REST Webservice (controllers) | `dotnet new genhttp-webservice-controllers` | [Controllers](https://genhttp.org/documentation/content/frameworks/controllers/) | -| Websocket | `dotnet new genhttp-websocket` | [Websockets](https://genhttp.org/documentation/content/frameworks/websockets/) | -| Server Sent Events (SSE) | `dotnet new genhttp-sse` | [Server Sent Events](https://genhttp.org/documentation/content/handlers/server-sent-events/) | -| Website (Static HTML) | `dotnet new genhttp-website-static` | [Statics Websites](https://genhttp.org/documentation/content/frameworks/static-websites/) | -| Single Page Application (SPA) | `dotnet new genhttp-spa` | [Single Page Applications (SPA)](https://genhttp.org/documentation/content/frameworks/single-page-applications/) | +```csharp +using GenHTTP.Engine.Internal; -After the project has been created, you can run it via `dotnet run` and access the server via http://localhost:8080. +using GenHTTP.Modules.ApiBrowsing; +using GenHTTP.Modules.Functional; +using GenHTTP.Modules.Layouting; +using GenHTTP.Modules.OpenApi; +using GenHTTP.Modules.Practices; -### Extending Existing Apps +// use a handler of your choice (see the samples below) +var api = Inline.Create() + .Get((int a, int b) => a + b); -If you would like to extend an existing .NET application, just add a nuget reference to the `GenHTTP.Core` nuget package. You can then spawn a new server instance with just a few lines of code: - -```csharp -var content = Content.From(Resource.FromString("Hello World!")); +var app = Layout.Create() + .Add(api) + .AddOpenApi() + .AddScalar(); var host = await Host.Create() - .Handler(content) + .Handler(app) .Defaults() - .StartAsync(); // or .RunAsync() to block until the application is shut down + .StartAsync(); // or .RunAsync() to block until the (console) application is shut down ``` -When you run this sample it can be accessed in the browser via http://localhost:8080. +Running this snippet will provide the following endpoints: + +| Endpoint | Description | +|------------------------------------|------------------------------------------------------------------------| +| http://localhost:8080?a=1&b=2 | Serves the API, answering requests by calculating the sum of two query arguments. | +| http://localhost:8080/openapi.json | Serves the automatically generated Open API specification of the API. | +| http://localhost:8080/scalar/ | Servers a graphical viewer of the API, using Scalar. | -### Next Steps +## Samples -The [documentation](https://genhttp.org/documentation/) provides a step-by-step starting guide as well as additional -information on how to -implement [webservices](https://genhttp.org/documentation/content/frameworks/webservices/), [minimal webservices](https://genhttp.org/documentation/content/frameworks/functional/), [controller-based webservices](https://genhttp.org/documentation/content/frameworks/controllers/), [static websites](https://genhttp.org/documentation/content/frameworks/static-websites/), -or [single page applications](https://genhttp.org/documentation/content/frameworks/single-page-applications/) and how -to [host your application](https://genhttp.org/documentation/hosting/) via Docker. +The [playground](./Playground/) project provides a quick starting point to view sample code and find more complex apps +built with GenHTTP. See [the documentation](https://genhttp.org/documentation/content/) for all available capabilities. ## Support @@ -79,24 +56,9 @@ to [join our Discord community](https://discord.gg/PRkwKrnrB4) to get help. For commercial products and projects, GenHTTP provides additional support options [on request](https://genhttp.org/support/). -## Platforms & Releases - -GenHTTP targets all .NET versions currently [supported by Microsoft](https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core). -Major versions are released once a year, following the .NET release cycle. -Additionally, our automated tests ensure full compatibility on the following platforms: - -| OS | Architectures | -|---------|-------------------------| -| Linux | `x64`, `arm32`, `arm64` | -| Windows | `x64`, `arm64` | -| macOS | `x64`, `arm64` | - ## Building the Server -> [!NOTE] -> The `main` branch reflects the upcoming changes for GenHTTP 11, which are rather drastic. For the current stable release, checkout `release/10.5` instead. - -To build the server from source, clone this repository and run the playground project launcher for .NET 10: +To build the server from source, clone this repository and run the playground project launcher for .NET 11: ```sh git clone https://github.com/Kaliumhexacyanoferrat/GenHTTP.git @@ -104,24 +66,9 @@ cd ./GenHTTP/Playground dotnet run ``` -This will build the playground project launcher with all the server dependencies and launch the server process on port 8080. You can access the playground in the browser via http://localhost:8080. - -## Contributing +This will build the playground project launcher with all the server dependencies and launch the server process on port -Writing a general purpose web application server is a tremendous task, so any contribution is very welcome. Besides -extending the server core, you might want to - -- Leave a star on GitHub -- Extend the content capabilities of the server (e.g. by adding a new serialization format or rendering engine) -- Refine our [project templates](https://genhttp.org/documentation/content/templates/) -- Perform code reviews -- Analyze the performance or security of the server -- Clarfify and extend our tests -- Improve the documentation on the [website](https://genhttp.org/) or in code - -If you would like to contribute, please also have a look at -the [contribution guidelines](https://github.com/Kaliumhexacyanoferrat/GenHTTP/blob/master/CONTRIBUTING.md) and -the [good first issues](https://github.com/Kaliumhexacyanoferrat/GenHTTP/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). +8080. You can access the playground in the browser via http://localhost:8080. ## History @@ -129,24 +76,16 @@ The web server was originally developed in 2008 to run on a netbook with an Inte failed to render dynamic pages on such a slow CPU back then. The original project description can still be found on [archive.org](https://web.archive.org/web/20100706192130/http://gene.homeip.net/GenHTTPWebsite/). In 2019, the source code has been moved to GitHub with the goal to rework the project to be able to run dockerized web applications written -in C#. In 2024 the focus has shifted towards API development, dropping support for generating graphical web applications. - -## Links - -- Related to - GenHTTP: [Templates](https://github.com/Kaliumhexacyanoferrat/GenHTTP.Templates) | [Website](https://github.com/Kaliumhexacyanoferrat/GenHTTP.Website) -- Reference - projects: [GenHTTP Gateway](https://github.com/Kaliumhexacyanoferrat/GenHTTP.Gateway) | [MockH](https://github.com/Kaliumhexacyanoferrat/MockH) | [LiquidPages](https://github.com/kinetq/liquid-pages) -- Similar - projects: [Wired.IO](https://github.com/MDA2AV/Wired.IO) | [Unhinged](https://github.com/MDA2AV/unhinged) | [SimpleW](https://github.com/stratdev3/SimpleW) | [Sisk](https://www.sisk-framework.org/) | [NetCoreServer](https://github.com/chronoxor/NetCoreServer) | [Watson Webserver](https://github.com/jchristn/WatsonWebserver) | [EmbedIO](https://github.com/unosquare/embedio) +in C#. In 2024 the focus has shifted towards API development, dropping support for generating graphical web +applications. In 2026, the API and internal engine have been rewritten to be allocation-free, greatly +improving performance in result. ## Thanks -- Powered by [.NET](https://github.com/dotnet/core) -- Modules implemented with [NSwag](https://github.com/RicoSuter/NSwag) | [Cottle](https://r3c.github.io/cottle/) | [SharpCompress](https://github.com/adamhathcock/sharpcompress) +- Powered by [.NET](https://github.com/dotnet/core) and the [.NET Web Stack](https://github.com/dotnet-web-stack) +- Modules implemented with [ioxide](https://github.com/MDA2AV/ioxide) | [NSwag](https://github.com/RicoSuter/NSwag) | [Cottle](https://r3c.github.io/cottle/) | [SharpCompress](https://github.com/adamhathcock/sharpcompress) +- Monitored by [HTTP Arena](https://www.http-arena.com) and [HTTP 1.1 Probe](https://www.http-probe.com/) ### Supported by -[![HTTP Arena logo.](https://cdn.jsdelivr.net/gh/MDA2AV/httparena-badge/wordmark.svg)]([https://jb.gg/OpenSource](https://www.http-arena.com/leaderboard/)) - [![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSource)