From 43fdf131fa7d85c3111e79d30183d3a5fe2007e7 Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Sun, 22 Feb 2026 04:53:24 +0800 Subject: [PATCH] Added AGENT.md and updated docs --- .gitea/workflows/build-dev.yaml | 2 - AGENTS.md | 173 ++++++++++++++++++++++++++++++++ build.gradle | 2 +- doc/server_configuration.md | 20 +++- 4 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 AGENTS.md diff --git a/.gitea/workflows/build-dev.yaml b/.gitea/workflows/build-dev.yaml index 0915e37..55aedb5 100644 --- a/.gitea/workflows/build-dev.yaml +++ b/.gitea/workflows/build-dev.yaml @@ -9,8 +9,6 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 - name: Execute Gradle build run: ./gradlew build - name: Prepare Docker image build diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..92925d1 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,173 @@ +# AGENTS.md — Coding Agent Guide for RBCS + +## Project Overview + +RBCS (Remote Build Cache Server) is a Kotlin/Java multi-module Gradle project built on Netty. +It serves as a remote build cache for Gradle and Maven. Java is used for the public API layer +(with Lombok); Kotlin is used for all implementation modules. All modules use JPMS +(`module-info.java`). The project is built on Netty with Java 25. + +**Modules:** `rbcs-api`, `rbcs-common`, `rbcs-server`, `rbcs-server-memcache`, `rbcs-client`, +`rbcs-cli`, `rbcs-servlet`, `docker` + +## Build Commands + +```bash +./gradlew build # Full build: compile + test + assemble +./gradlew assemble # Build JARs without running tests +./gradlew compileJava compileKotlin # Compile only +./gradlew clean # Clean build outputs +./gradlew :rbcs-server:compileKotlin # Compile a single module +./gradlew -q version # Print project version +``` + +## Test Commands + +```bash +./gradlew test # Run all tests (all modules) +./gradlew :rbcs-server:test # Run tests for a single module +./gradlew :rbcs-server:test --tests "net.woggioni.rbcs.server.test.NoAuthServerTest" + # Run a single test class +./gradlew :rbcs-server:test --tests "net.woggioni.rbcs.server.test.NoAuthServerTest.putWithNoAuthorizationHeader" + # Run a single test method +./gradlew test --tests "*TlsServer*" # Run tests matching a pattern +./gradlew :rbcs-server:jacocoTestReport # Generate code coverage (rbcs-server only) +``` + +**Test framework:** JUnit 5 (Jupiter). Tests are integration-style — they start real Netty +servers and use `java.net.http.HttpClient` to make HTTP requests. Netty leak detection is +set to `PARANOID` in test configurations. + +**Test locations:** `src/test/kotlin/` and `src/test/java/` following standard Gradle layout. +Test resources (XML configs, logback) are in `src/test/resources/`. + +## Lint / Format + +No linting or formatting tools are configured (no Checkstyle, Detekt, ktlint, Spotless, or +similar). Follow the existing code style described below. + +## Code Style Guidelines + +### Language Split + +- **Java** for `rbcs-api` (public API consumed by Java clients) with Lombok annotations +- **Kotlin** for all implementation modules +- **`module-info.java`** in every module (JPMS) + +### Import Ordering (Kotlin) + +Three groups separated by blank lines, each alphabetically sorted: +1. External/third-party (`io.netty.*`, `org.slf4j.*`) +2. Java standard library (`java.*`, `javax.*`) +3. Internal project (`net.woggioni.rbcs.*`) + +Import aliases are rare; used only to resolve conflicts: +`import io.netty.util.concurrent.Future as NettyFuture` + +### Naming Conventions + +| Element | Convention | Examples | +|---------------------|-------------------------|---------------------------------------------------| +| Classes/interfaces | PascalCase | `RemoteBuildCacheServer`, `CacheHandler` | +| Abstract classes | `Abstract` prefix | `AbstractServerTest`, `AbstractNettyHttpAuthenticator` | +| Functions/methods | camelCase | `loadConfiguration()`, `sendShutdownSignal()` | +| Variables/properties| camelCase | `bucketManager`, `sslContext` | +| Constants | SCREAMING_SNAKE_CASE | `SSL_HANDLER_NAME`, `RBCS_NAMESPACE_URI` | +| Handler names | `val NAME = ...::class.java.name` in companion object | +| Packages | lowercase dot-separated | `net.woggioni.rbcs.server.throttling` | +| Enum values | PascalCase | `Role.Reader`, `Role.Writer` | +| Kotlin files | PascalCase matching primary class; lowercase for files with only top-level functions | + +### Error Handling + +- Custom unchecked exception hierarchy rooted at `RbcsException extends RuntimeException` +- Domain subclasses: `CacheException`, `ConfigurationException`, `ContentTooLargeException` +- Async errors propagated via `CompletableFuture.completeExceptionally()` +- Synchronous errors thrown directly: `throw IllegalArgumentException(...)` +- Kotlin null safety idioms preferred over null checks: `?.let`, `?:`, `takeIf` +- `ExceptionHandler` maps exception types to HTTP responses via exhaustive `when` + +### Type Annotations + +- **Kotlin:** Heavy use of type inference for local variables. Explicit types required on: + - Class properties: `private val sslContext: SslContext?` + - Non-trivial return types: `fun run(): ServerHandle` + - `lateinit var` in tests: `protected lateinit var cfg: Configuration` +- **Java:** Lombok `@Value` for immutable data classes; modern pattern matching with `instanceof` +- No `typealias` declarations are used in this project + +### Async Patterns + +- Primary abstraction: `CompletableFuture` — **no Kotlin coroutines** +- Netty event-driven callbacks (`ChannelFuture`, `GenericFutureListener`) +- Custom `AsyncCloseable` interface wraps `CompletableFuture` for async shutdown +- Retry logic uses `CompletableFuture` composition with exponential backoff + jitter +- Virtual threads used selectively (background GC, configurable event executors) +- Connection pooling via Netty `FixedChannelPool` + +### Logging + +- SLF4J via custom Kotlin lazy-evaluation extension functions (defined in `rbcs-common`) +- Logger creation: `private val log = createLogger()` (in companion object) +- Lazy log calls: `log.debug { "message with ${interpolation}" }` (lambda only evaluated if enabled) +- Channel-aware variants: `log.debug(ctx) { "message" }` (adds MDC: channel-id, remote-address) +- Java classes use Lombok `@Slf4j` +- String templates with `${}` for Kotlin log messages + +### Kotlin Idioms + +- `apply` as builder pattern: `ServerBootstrap().apply { group(...); option(...) }` +- `also` for side effects, `let` for transformation, `run` for scoping +- Trailing commas in constructor parameter lists and multi-line function calls +- `sealed class`/`sealed interface` for algebraic types (e.g., `OperationOutcome`, `Authentication`) +- `data class` for value types; `companion object` for static members and constants +- No trailing semicolons + +### Java Idioms (API module) + +- Lombok annotations: `@Value`, `@Getter`, `@RequiredArgsConstructor`, `@EqualsAndHashCode.Include` +- `sealed interface` with `final` permitted subtypes (e.g., `CacheMessage`) +- `@FunctionalInterface` on single-method interfaces +- JPMS `module-info.java` in every module with `requires`, `exports`, `uses`, `provides` + +### Testing Patterns + +- `@TestInstance(Lifecycle.PER_CLASS)` — single instance per test class +- `@TestMethodOrder(MethodOrderer.OrderAnnotation)` with `@Order(n)` for sequential execution +- Abstract base class hierarchy: `AbstractServerTest` → `AbstractBasicAuthServerTest` → concrete +- Server lifecycle in `@BeforeAll` / `@AfterAll` (start/stop real Netty server) +- `@TempDir` for temporary directories +- `@ParameterizedTest` with `@ValueSource` and `@ArgumentsSource` for parameterized tests +- Assertions via `org.junit.jupiter.api.Assertions` (`assertEquals`, `assertArrayEquals`) +- No mocking framework — all tests are integration-style against real servers + +### Documentation + +Minimal doc comments in the codebase. Inline comments used sparingly for clarification. +When adding new public API, follow existing style — doc comments are not enforced but +are welcome on complex logic. + +## Module Dependency Graph + +``` +rbcs-api → (standalone, Lombok, Netty types) +rbcs-common → Netty, Kotlin stdlib +rbcs-server → rbcs-api, rbcs-common +rbcs-server-memcache → rbcs-api, rbcs-common +rbcs-client → rbcs-api, rbcs-common +rbcs-cli → rbcs-client, rbcs-server (Picocli for CLI) +rbcs-servlet → rbcs-api, rbcs-common (Jakarta Servlet/CDI) +docker → rbcs-cli, rbcs-server-memcache +``` + +## Plugin System + +Cache backends use the `ServiceLoader` pattern via JPMS `provides`/`uses` directives. +To add a new cache provider, implement `CacheProvider` and +register it in your module's `module-info.java`. + +## Configuration + +- XML-based with XSD schema validation +- Schemas in `src/main/resources/.../schema/*.xsd` +- Default config loaded from JPMS resources: `jpms://net.woggioni.rbcs.server/...` diff --git a/build.gradle b/build.gradle index 6c4dcc3..5deca16 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ allprojects { subproject -> mavenCentral() } - pluginManager.withPlugin('java-library') { + pluginManager.withPlugin('java') { ext { jpmsModuleName = subproject.group + '.' + subproject.name.replace('-', '.') diff --git a/doc/server_configuration.md b/doc/server_configuration.md index a10ddbb..1874fc8 100644 --- a/doc/server_configuration.md +++ b/doc/server_configuration.md @@ -14,8 +14,21 @@ Configures server socket settings. **Attributes:** - `host` (required): Server bind address - `port` (required): Server port number +- `proxy-protocol` (optional, default: false): Enable [HAProxy proxy protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) support. When enabled, the server decodes proxy protocol headers to extract the real client IP address from proxied connections. - `incoming-connections-backlog-size` (optional, default: 1024): Maximum queue length for incoming connection indications +**Child Elements:** + +##### `` +Restricts which proxy servers are trusted to provide accurate client IP information via the proxy protocol. Only used when `proxy-protocol` is set to `true`. + +If omitted or empty, all proxies are trusted. When specified, only connections originating from the listed CIDR ranges will have their forwarded client IP honored. + +- Contains `` elements: + + **Attributes:** + - `cidr` (required): An IPv4 or IPv6 CIDR range identifying a trusted proxy address (e.g. `192.168.0.0/24`, `::1/128`) + #### `` Configures connection handling parameters. @@ -127,7 +140,12 @@ Configures TLS encryption. xmlns:rbcs="urn:net.woggioni.rbcs.server" xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs-server.xsd" > - + + + + + +