From 4be82b3a66f87b47b6744a5c6db379189a4f679f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20N=C3=A4geli?= Date: Mon, 5 Oct 2026 09:58:56 +0200 Subject: [PATCH] Explain the .NET / GenHTTP context and document ASP.NET Core hosting Readers unfamiliar with GenHTTP could not tell that this is a C# library that plugs into a web server. The readmes and the documentation now introduce the package as a GenHTTP module, show how to set up a project from scratch and describe how to map the handler into an ASP.NET Core application via GenHTTP.Adapters.AspNetCore, including the Kestrel request body limit that otherwise rejects pushes above 30 MB. Co-Authored-By: Claude Opus 5.5 --- Module/README.md | 48 +++++++++++++++++-- README.md | 103 +++++++++++++++++++++++++++++++++++++--- docs/aspnet-core.md | 93 ++++++++++++++++++++++++++++++++++++ docs/getting-started.md | 17 +++++-- docs/hosting.md | 3 +- docs/index.md | 23 +++++++-- mkdocs.yml | 3 +- 7 files changed, 269 insertions(+), 21 deletions(-) create mode 100644 docs/aspnet-core.md diff --git a/Module/README.md b/Module/README.md index 0df2e34..f249585 100644 --- a/Module/README.md +++ b/Module/README.md @@ -1,18 +1,27 @@ # GenHTTP.Modules.Git -Serves **virtual git repositories** from a [GenHTTP](https://genhttp.org/) handler. Clones, fetches and pushes are -answered from your own data model via the smart HTTP protocol - without a repository on disk and without the `git` -binary. +A C# library that turns your .NET application into a **git server for your own data**. Users clone, fetch and push +with the regular `git` client, while every request is answered from your data model (a database, a blob store, an +API, ...) - without a repository on disk and without the `git` binary on the server. Use it to offer versioned content (documents, configurations, apps, snippets) to developers, CI pipelines and agents that know how to use git. +The package is a module for [GenHTTP](https://genhttp.org/), a lightweight, embeddable web server framework for +.NET. It provides a request handler that speaks git's smart HTTP protocol, which you can host in a GenHTTP +application or map to a path of an existing ASP.NET Core application. + ## Getting started -Add the package to a project hosting a GenHTTP server: +Create a console application and add the module along with a GenHTTP engine, which runs the server: ```sh +dotnet new console -n GitDemo +cd GitDemo + dotnet add package GenHTTP.Modules.Git +dotnet add package GenHTTP.Engine.Internal +dotnet add package GenHTTP.Modules.Practices ``` Serve a repository via `GitServer.Create()`. The `InMemoryGitRepository` behaves like a bare repository and is a good @@ -43,6 +52,35 @@ await Host.Create() git clone http://localhost:8080/ hello ``` +## Using ASP.NET Core + +With the [GenHTTP.Adapters.AspNetCore](https://www.nuget.org/packages/GenHTTP.Adapters.AspNetCore/) package, the +handler can be mapped to a path of an existing ASP.NET Core application: + +```sh +dotnet add package GenHTTP.Modules.Git +dotnet add package GenHTTP.Adapters.AspNetCore +``` + +```csharp +using GenHTTP.Adapters.AspNetCore; + +using GenHTTP.Modules.Git; + +var builder = WebApplication.CreateBuilder(args); + +// Kestrel accepts 30 MB request bodies by default, allow larger pushes +builder.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = 128 * 1024 * 1024); + +var app = builder.Build(); + +// serve the repository (created as shown above) at /repo, +// all other requests are handled by your application +app.Map("/repo", GitServer.Create().Repository(repository)); + +await app.RunAsync(); +``` + ## Serving your own data Implement `IGitRepository` to describe your data as branches, tags, commits and files: @@ -101,7 +139,7 @@ public async ValueTask PushAsync(GitPush push) - Truncated histories (e.g. deleted old versions) are served as shallow repositories - Pushes with deltas, push options (`git push -o`) and messages - Regular files, executables and symbolic links, loaded lazily -- Single or multiple repositories per handler, all GenHTTP engines and concerns (e.g. authentication) +- Single or multiple repositories per handler, all GenHTTP engines and concerns (e.g. authentication), ASP.NET Core - Protection against crafted requests (size and file limits, validated file names) Annotated tags, submodules, partial clones (`--filter`), the dumb HTTP protocol and SHA-256 repositories are not diff --git a/README.md b/README.md index de85b2b..b4c6989 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,45 @@ # Virtual Git Server -A [GenHTTP](https://genhttp.org/) module that serves **virtual git repositories**: clones, fetches and pushes are -answered from your own data model over the smart HTTP protocol, without a repository on disk and without the `git` -binary. Use it to expose versioned content (documents, configurations, apps, snippets) to developers, CI pipelines and -agents that know how to use git. +A C# library that turns your .NET application into a **git server for your own data**. Users clone, fetch and push +with the regular `git` client, while every request is answered from your data model (a database, a blob store, an +API, ...) - without a repository on disk and without the `git` binary on the server. + +Use it to expose versioned content (documents, configurations, apps, snippets) to developers, CI pipelines and coding +agents that know how to use git, instead of building custom sync tools. [![nuget Package](https://img.shields.io/nuget/v/GenHTTP.Modules.Git.svg)](https://www.nuget.org/packages/GenHTTP.Modules.Git/) [![Build](https://github.com/Kaliumhexacyanoferrat/virtual-git-server/actions/workflows/build.yml/badge.svg)](https://github.com/Kaliumhexacyanoferrat/virtual-git-server/actions/workflows/build.yml) [![View - Documentation](https://img.shields.io/badge/view-Documentation-AB54FF)](https://kaliumhexacyanoferrat.github.io/virtual-git-server/) +## How it fits in + +The library is distributed as the NuGet package `GenHTTP.Modules.Git`. It is a module for +[GenHTTP](https://genhttp.org/), a lightweight, embeddable web server framework for .NET: the module provides a +request handler that speaks git's smart HTTP protocol, and you decide where in your web application it lives. + +You can use it in two ways: + +- **In a GenHTTP application** - the module plugs into a GenHTTP server like any other handler, either as the only + content or next to websites, APIs and files ([Getting Started](#getting-started)). +- **In an ASP.NET Core application** - an adapter maps the handler to a path of your existing `WebApplication`, next + to your controllers and minimal APIs ([Using ASP.NET Core](#using-aspnet-core)). + +In both cases, your code describes the repository (branches, commits and files) and decides what to do with pushes; +the module handles the git protocol. + ## Getting Started -Add the `GenHTTP.Modules.Git` package to your project and serve a repository via `GitServer.Create()`: +You need the [.NET SDK](https://dotnet.microsoft.com/download) (10 or newer). Create a console application and add +the module along with a GenHTTP engine to run the server: + +```sh +dotnet new console -n GitDemo +cd GitDemo + +dotnet add package GenHTTP.Modules.Git +dotnet add package GenHTTP.Engine.Internal +dotnet add package GenHTTP.Modules.Practices +``` + +Replace the content of `Program.cs` to serve a repository via `GitServer.Create()`: ```csharp using GenHTTP.Engine.Internal; @@ -17,26 +47,85 @@ using GenHTTP.Engine.Internal; using GenHTTP.Modules.Git; using GenHTTP.Modules.Practices; +// a repository that lives in memory, useful for trying things out var repository = new InMemoryGitRepository(); await repository.CommitAsync("main", GitTree.Create() .Add("README.md", "# Hello World") .Build(), "Initial commit"); +// the handler answering git requests var git = GitServer.Create() .Repository(repository); +// a GenHTTP server listening on port 8080 await Host.Create() .Handler(git) .Defaults() .RunAsync(); ``` +Start the application with `dotnet run` and use git as with any other server: + ```sh git clone http://localhost:8080/ hello ``` -To serve your own data, implement `IGitRepository` (read-only) or `IWritableGitRepository` (accepting pushes): +To combine the repository with other content, add it to a layout from the `GenHTTP.Modules.Layouting` package +(e.g. `Layout.Create().Add("repo", git)` to serve it at `/repo`). See the [GenHTTP documentation](https://genhttp.org/documentation/) for everything else a GenHTTP +server can do. + +## Using ASP.NET Core + +If you already have an ASP.NET Core application, the +[GenHTTP.Adapters.AspNetCore](https://www.nuget.org/packages/GenHTTP.Adapters.AspNetCore/) package lets you map the +git handler to a path, so you do not need to run a separate server: + +```sh +dotnet add package GenHTTP.Modules.Git +dotnet add package GenHTTP.Adapters.AspNetCore +``` + +```csharp +using GenHTTP.Adapters.AspNetCore; + +using GenHTTP.Modules.Git; + +var repository = new InMemoryGitRepository(); + +await repository.CommitAsync("main", GitTree.Create() + .Add("README.md", "# Hello World") + .Build(), "Initial commit"); + +var builder = WebApplication.CreateBuilder(args); + +// Kestrel accepts 30 MB request bodies by default, allow larger pushes +builder.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = 128 * 1024 * 1024); + +var app = builder.Build(); + +// serve the repository at /repo +app.Map("/repo", GitServer.Create().Repository(repository)); + +// the rest of your application +app.MapGet("/", () => "Hello from ASP.NET Core"); + +await app.RunAsync(); +``` + +```sh +# use the address your application listens on +git clone http://localhost:5000/repo hello +``` + +The handler only answers requests below the mapped path, all other requests are passed on to the rest of your +application. The [documentation](https://kaliumhexacyanoferrat.github.io/virtual-git-server/aspnet-core/) covers +multiple repositories and authentication with ASP.NET Core. + +## Serving your own data + +The in-memory repository is a good start. To serve your own data, implement `IGitRepository` (read-only) or +`IWritableGitRepository` (accepting pushes): ```csharp public sealed class DocumentRepository : IWritableGitRepository @@ -69,7 +158,7 @@ commit ids stable, how pushes are validated and shows a complete example of a ve | Pushing | Creating, updating and deleting branches and tags, deltas, push options, `remote:` messages | | History | Truncated histories (e.g. deleted old versions) are served as shallow repositories | | Files | Regular files, executables and symbolic links, loaded on demand | -| Hosting | Single or multiple repositories, any GenHTTP engine, concerns such as authentication | +| Hosting | Single or multiple repositories, any GenHTTP engine or ASP.NET Core, concerns such as authentication | Not supported: annotated tags, submodules, partial clones (`--filter`), the dumb HTTP protocol and SHA-256 repositories. See [protocol support](https://kaliumhexacyanoferrat.github.io/virtual-git-server/protocol/) for details. diff --git a/docs/aspnet-core.md b/docs/aspnet-core.md new file mode 100644 index 0000000..75f1aa6 --- /dev/null +++ b/docs/aspnet-core.md @@ -0,0 +1,93 @@ +# ASP.NET Core + +The module is a regular GenHTTP handler, so it does not require a GenHTTP server. With the +[GenHTTP.Adapters.AspNetCore](https://www.nuget.org/packages/GenHTTP.Adapters.AspNetCore/) package, it can be +mapped to a path of an existing ASP.NET Core application and runs next to your controllers, Razor pages and +minimal APIs. + +## Installation + +```sh +dotnet add package GenHTTP.Modules.Git +dotnet add package GenHTTP.Adapters.AspNetCore +``` + +## Mapping a repository + +The adapter adds a `Map` overload to `WebApplication` that accepts GenHTTP handlers: + +```csharp +using GenHTTP.Adapters.AspNetCore; + +using GenHTTP.Modules.Git; + +var repository = new InMemoryGitRepository(); + +await repository.CommitAsync("main", GitTree.Create() + .Add("README.md", "# Hello World") + .Build(), "Initial commit"); + +var builder = WebApplication.CreateBuilder(args); + +builder.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = 128 * 1024 * 1024); + +var app = builder.Build(); + +app.Map("/repo", GitServer.Create().Repository(repository)); + +app.MapGet("/", () => "Hello from ASP.NET Core"); + +await app.RunAsync(); +``` + +```sh +# use the address your application listens on +git clone http://localhost:5000/repo hello +``` + +Requests below the mapped path are handled by the git server, all other requests continue through the ASP.NET Core +pipeline. + +The builder returned by `GitServer.Create()` is configured exactly as described in [Hosting](hosting.md), so limits, +resolvers and concerns work the same way. + +## Request size + +Kestrel rejects request bodies larger than 30 MB by default, while the module accepts pushes up to 128 MB +(`MaximumPushSize`). Larger pushes then fail with HTTP 413 before they reach the module, so raise the Kestrel limit +to at least the configured push size, as shown above. If the application runs behind IIS or a reverse proxy, their +limits apply as well. + +## Multiple repositories + +Resolvers work the same way as with a GenHTTP server. The path segment below the mapped path selects the +repository: + +```csharp +app.Map("/git", GitServer.Create() + .Repositories(async (request, name) => await store.FindRepositoryAsync(name))); + +// git clone http://localhost:5000/git/my-project +// git clone http://localhost:5000/git/my-project.git +``` + +## Authentication + +GenHTTP concerns can be added to the builder, so the [authentication](hosting.md#authentication) described for +GenHTTP servers also works within ASP.NET Core: + +```sh +dotnet add package GenHTTP.Modules.Authentication +``` + +```csharp +using GenHTTP.Modules.Authentication; + +app.Map("/repo", GitServer.Create() + .Repository(repository) + .Add(BasicAuthentication.Create().Add("jane", "secret"))); +``` + +The handler is invoked by the adapter outside of the ASP.NET Core routing, so endpoint metadata such as +`RequireAuthorization()` does not apply to it. Use a GenHTTP concern or a middleware registered before the mapping +to protect the repository. diff --git a/docs/getting-started.md b/docs/getting-started.md index cb89d34..f05d217 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,14 +1,25 @@ # Getting Started +The module is a .NET library that runs within a [GenHTTP](https://genhttp.org/) web server. This page creates a +small console application hosting such a server. If you would like to add a repository to an existing ASP.NET Core +application instead, see [ASP.NET Core](aspnet-core.md). + ## Installation -Add the module to a project hosting a GenHTTP server: +You need the [.NET SDK](https://dotnet.microsoft.com/download) (10 or newer). Create a console application and add +the module along with a GenHTTP engine, which runs the server: ```sh +dotnet new console -n GitDemo +cd GitDemo + dotnet add package GenHTTP.Modules.Git +dotnet add package GenHTTP.Engine.Internal +dotnet add package GenHTTP.Modules.Practices ``` -The module targets .NET 10 and .NET 11 and works with every GenHTTP engine (internal, Kestrel and ioxide). +The module targets .NET 10 and .NET 11 and works with every GenHTTP engine (internal, Kestrel and ioxide). The +`Practices` package adds recommended defaults such as compression to the server. ## Serving a repository @@ -39,7 +50,7 @@ await Host.Create() .RunAsync(); ``` -Run the project and use git as you would with any other server: +Run the project with `dotnet run` and use git as you would with any other server: ```sh git clone http://localhost:8080/ hello diff --git a/docs/hosting.md b/docs/hosting.md index 8ca4f45..326cf25 100644 --- a/docs/hosting.md +++ b/docs/hosting.md @@ -92,7 +92,8 @@ searched for in the newest 10,000 commits of the repository. ## Engines and concerns -The handler works with all GenHTTP engines. Concerns such as compression or client caching added via `Defaults()` do +The handler works with all GenHTTP engines and can also be used within an ASP.NET Core application, see +[ASP.NET Core](aspnet-core.md). Concerns such as compression or client caching added via `Defaults()` do not interfere with git: responses are marked as not cacheable, and request bodies compressed by git are detected regardless of whether a decompression concern already handled them. diff --git a/docs/index.md b/docs/index.md index 0d9f828..2bf380e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,8 +1,8 @@ # Virtual Git Server -`GenHTTP.Modules.Git` serves **virtual git repositories** from a [GenHTTP](https://genhttp.org/) handler. Every -clone, fetch and push is answered from your own data model over the smart HTTP protocol, with no repository on disk -and no `git` binary. +A C# library that turns your .NET application into a **git server for your own data**. Users clone, fetch and push +with the regular `git` client, while every request is answered from your data model (a database, a blob store, an +API, ...) - with no repository on disk and no `git` binary on the server. ```csharp var git = GitServer.Create() @@ -17,6 +17,20 @@ await Host.Create() git clone http://localhost:8080/ documents ``` +## How it fits in + +The library is distributed as the NuGet package `GenHTTP.Modules.Git`. It is a module for +[GenHTTP](https://genhttp.org/), a lightweight, embeddable web server framework for .NET: the module provides a +request handler that speaks git's smart HTTP protocol, and you decide where in your web application it lives. + +- **In a GenHTTP application**, the handler is added to the server like any other content, either on its own or + next to websites, APIs and files. See [Getting Started](getting-started.md). +- **In an ASP.NET Core application**, an adapter maps the handler to a path of your existing `WebApplication`. See + [ASP.NET Core](aspnet-core.md). + +In both cases, your code describes the repository (branches, commits and files) and decides what to do with pushes, +while the module handles the git protocol. + ## Why? Git is the interface developers, CI pipelines and coding agents already know. If your application manages versioned @@ -41,13 +55,14 @@ source of truth (your data model) and derives everything git needs from it on th | Pushing | Creating, updating and deleting branches and tags, deltas, push options, `remote:` messages | | History | Truncated histories (e.g. deleted old versions) are served as shallow repositories | | Files | Regular files, executables and symbolic links, loaded on demand | -| Hosting | Single or multiple repositories, any GenHTTP engine, concerns such as authentication | +| Hosting | Single or multiple repositories, any GenHTTP engine or ASP.NET Core, concerns such as authentication | ## Where to go next
- **[Getting Started](getting-started.md)**: serve your first repository in a few lines of code. +- **[ASP.NET Core](aspnet-core.md)**: add a repository to an existing ASP.NET Core application. - **[How It Works](concepts.md)**: references, commits, trees and why commit ids must never change. - **[Serving Content](repositories.md)**: implement `IGitRepository` for your data model. - **[Accepting Pushes](pushing.md)**: implement `IWritableGitRepository` and validate what clients send. diff --git a/mkdocs.yml b/mkdocs.yml index 3a820d2..4e5d4f8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,5 @@ site_name: Virtual Git Server -site_description: Serve virtual git repositories from your own data model with GenHTTP. +site_description: Serve virtual git repositories from your own data model in .NET applications, with GenHTTP or ASP.NET Core. site_url: https://kaliumhexacyanoferrat.github.io/virtual-git-server/ site_author: Andreas Nägeli @@ -67,6 +67,7 @@ nav: - Serving Content: repositories.md - Accepting Pushes: pushing.md - Hosting: hosting.md + - ASP.NET Core: aspnet-core.md - Testing: testing.md - Examples: - Versioned Content: examples/versioned-content.md