Skip to content

dotnet-web-stack/CodeGreen

Repository files navigation

CodeGreen

A 1:1 port of GenHTTP to Kotlin/JVM.

The goal is to replicate GenHTTP's public API, module set, and architectural concepts in idiomatic Kotlin - coroutines, NIO channels, and suspend functions where GenHTTP uses async/await and System.IO.Pipelines. The resulting behaviour is intended to be identical: the same routing model, the same webservice reflection framework, the same layouting and content modules.

This project is AI-generated and serves as a proof of concept to explore how a full framework port can be produced with AI assistance and to measure where Kotlin lands on throughput benchmarks relative to the .NET original.

No packages are published to Maven Central at this time. A local build is required (see Building from source below).


What is GenHTTP?

GenHTTP is a lightweight .NET web framework with a small footprint and a straightforward API. Its documentation covers the full module set - webservices, controllers, functional handlers, layouting, static content, websockets, and more. The concepts described there map directly to this port; the Kotlin equivalents carry the same names and behave the same way.


Quickstart

The example below demonstrates the core of the webservice module: returning primitives, serializing data classes to JSON, and consuming a raw request body stream. It mirrors the pattern described in the GenHTTP webservices documentation.

import kotlinx.coroutines.runBlocking
import org.codegreen.engine.internal.Host
import org.codegreen.modules.layouting.Layout
import org.codegreen.modules.webservices.addService
import org.codegreen.modules.reflection.Method
import org.codegreen.modules.webservices.ResourceMethod
import java.io.InputStream

// Data classes are serialised to JSON automatically.
data class Item(val id: Int, val name: String, val price: Double)

class CatalogService {

    private val store = mapOf(
        1 to Item(1, "Notebook", 9.99),
        2 to Item(2, "Pen", 1.49),
    )

    // GET /catalog/count  →  plain integer in the response body
    @ResourceMethod("count")
    fun count(): Int = store.size

    // GET /catalog/:id  →  JSON-serialised Item
    @ResourceMethod(":id")
    fun get(id: Int): Item? = store[id]

    // POST /catalog/upload  →  consumes the request body stream, returns byte count
    @ResourceMethod("upload", Method.Post)
    fun upload(body: InputStream): Long = body.readBytes().size.toLong()
}

fun main() = runBlocking {
    val layout = Layout.create()
        .addService<CatalogService>("catalog")

    Host.create()
        .handler(layout)
        .console()
        .run()
}

Running the example

Build and run the snippet above (or adapt it inside the :playground module):

./gradlew :playground:run

The server starts on http://localhost:8080. Open the following URLs in a browser:

URL Returns
http://localhost:8080/catalog/count 2
http://localhost:8080/catalog/1 {"id":1,"name":"Notebook","price":9.99}

For the stream endpoint, use curl:

curl -X POST http://localhost:8080/catalog/upload --data-binary @somefile
# returns the number of bytes received

Press Ctrl-C to stop the server.


Building from source

Requires JDK 17 or later (the Gradle toolchain targets 21).

./gradlew build           # compile all modules and run the engine acceptance tests
./gradlew :glyph11:test   # run the hardened HTTP/1.1 parser suite only

On a machine without a system JDK, install one via SDKMAN:

sdk install java 21-tem

Project structure

The module layout mirrors GenHTTP's solution structure:

Directory Gradle project Mirrors
API/ :api GenHTTP.Api - core abstractions and the MemoryView type
Engine/Shared/ :engine:shared GenHTTP.Engine.Shared - engine-agnostic request/response/hosting types
Engine/Internal/ :engine:internal GenHTTP.Engine.Internal - the HTTP/1.1 engine (NIO + coroutines)
Glyph11/ :glyph11 The hardened HTTP/1.1 parser, exposing the buffer as MemoryView
Testing/ :testing GenHTTP.Testing - the TestHost acceptance harness
Playground/ :playground GenHTTP.Playground - a runnable demo (see below)

Within each module the public API lives at the package root; implementations are in sub-packages (e.g. org.codegreen.engine.shared.types).


Playground

The :playground module is modelled on the HttpArena GenHTTP benchmark entry, rebuilt on the Conversion + Reflection + Webservices + Layouting stack. It runs with no external dependencies (the original's PostgreSQL layer is replaced with an in-memory store).

./gradlew :playground:run

Sample requests:

# plain-text pipeline
curl http://localhost:8080/pipeline

# sum two query parameters; POST variant adds a value from the body
curl "http://localhost:8080/baseline11?a=20&b=22"
curl -X POST "http://localhost:8080/baseline11?a=20&b=22" -d '8'

# JSON response with optional multiplier
curl "http://localhost:8080/json/3"
curl "http://localhost:8080/json/3?m=2"

# upload: returns byte count of the request body
curl -X POST http://localhost:8080/upload --data-binary @somefile

# range query
curl "http://localhost:8080/async-db?min=10&max=12"

# CRUD endpoints
curl "http://localhost:8080/crud/items?category=electronics&page=1&limit=2"
curl -i "http://localhost:8080/crud/items/5"
curl -X POST http://localhost:8080/crud/items \
     -H "Content-Type: application/json" \
     -d '{"id":5000,"name":"Widget","category":"tools","price":9,"quantity":3}'
curl -X PUT http://localhost:8080/crud/items/5 \
     -H "Content-Type: application/json" \
     -d '{"name":"Renamed","price":99,"quantity":7}'

To run without Gradle attached:

./gradlew :playground:installDist
./Playground/build/install/playground/bin/playground

Further reading

About

Experimental GenHTTP port to Kotlin

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages