Cette page est la référence canonique de Chappe : artefacts Maven, modules JPMS exportés, API publique majeure, fichier de configuration YAML de la CLI, flags de la CLI, plugin de pré-compression. Pour la conformité RFC, voir Concepts et chappe-conformance dans le repo.

Artefacts Maven

Artefact Rôle

io.vidocq.chappe:chappe-api:0.1.0-SNAPSHOT

API publique : Server, Router, Handler, Filter, Request, Response, Body, StatusCode, MimeTypes, StaticFileHandler, AcceptEncoding, RequestContext, ServerProvider.

io.vidocq.chappe:chappe-http:0.1.0-SNAPSHOT

Couche HTTP/1.1 + HTTP/2, TLS, ByteBufferPool, HPACK.

io.vidocq.chappe:chappe-core:0.1.0-SNAPSHOT

Moteur serveur (virtual threads, protocol detection, lifecycle, ChappeServerProvider).

io.vidocq.chappe:chappe-cli:0.1.0-SNAPSHOT

Launcher CLI standalone chappe serve (fat jar + scripts).

io.vidocq.chappe:chappe-tests:0.1.0-SNAPSHOT

Tests d’intégration (113 tests).

io.vidocq.chappe:chappe-conformance:0.1.0-SNAPSHOT

Conformité HTTP (45 tests RFC 9110/9112/9113).

io.vidocq.chappe:chappe-bench:0.1.0-SNAPSHOT

Benchmarks comparatifs (Jetty, Helidon, JDK HttpServer).

io.vidocq.chappe:chappe-static-index-maven-plugin:0.1.0-SNAPSHOT

Plugin Maven : index O(1) + sidecars .gz au build (option <compress>gzip</compress>).

io.vidocq.chappe:chappe-examples:0.1.0-SNAPSHOT

Exemples d’utilisation.

Modules JPMS

Module Exports / declarations

io.vidocq.chappe.api

exports io.vidocq.chappe.api;
uses io.vidocq.chappe.api.ServerProvider;

io.vidocq.chappe.http

requires io.vidocq.chappe.api;
exports io.vidocq.chappe.http;
exports io.vidocq.chappe.http.h2;

io.vidocq.chappe.core

requires io.vidocq.chappe.api;
requires io.vidocq.chappe.http;
exports io.vidocq.chappe.core;
provides io.vidocq.chappe.api.ServerProvider with io.vidocq.chappe.core.ChappeServerProvider;

io.vidocq.chappe.cli

requires io.vidocq.chappe.api;
requires io.vidocq.chappe.core;
requires java.net.http;

chappe-api n'`opens` rien. Aucun module n’expose son interne via réflexion : tout passe par les exports documentés ci-dessus.

API publique majeure (io.vidocq.chappe.api)

Server

public sealed interface Server extends AutoCloseable {
    void start();
    void stop();
    int port();
    static ServerBuilder builder();
}

ServerBuilder :

  • port(int)

  • bindAddress(String)

  • tls(SSLContext)

  • alpnProtocols(String…​)

  • handler(Handler)

  • executor(Executor) (par défaut : virtual threads)

  • build() retourne un Server

Router

Router.builder()
    .get(path, handler)
    .post(path, handler)
    .put(path, handler)
    .delete(path, handler)
    .patch(path, handler)
    .head(path, handler)            // auto pour GET aussi
    .options(path, handler)
    .group(prefix, sub -> sub.filter(...).get(...))
    .mount(prefix, handler)
    .filter(filter)
    .notFound(handler)
    .build();

Comportements :

  • 405 Method Not Allowed automatique avec header Allow quand le path matche mais pas la méthode.

  • Trailing slash transparent : /users et /users/ matchent la même route.

  • Percent-decoding sur path params : /users/John%20DoepathParam("id") = "John Doe".

Filter

public interface Filter {
    Handler apply(Handler next);

    static Filter addHeader(String name, String value);
    static Filter addHeaderIf(BooleanSupplier when, String name, String value);
    static Filter addHeaderIfEnv(String envVar, String expected, String name, String value);
    static Filter gzip();
    static Filter gzip(int threshold);
    static Filter accessLog();                          (1)
    static Filter accessLog(Consumer<String> sink);     (2)

    default Filter andThen(Filter next);
}
1 Logue chaque requête sur System.out au format Apache Combined Log Format étendu (cf. Observabilité).
2 Variante avec sink personnalisé (utile pour rediriger vers un logger applicatif).

BuildInfo

public final class BuildInfo {
    public static String version();          // ex: "0.1.0-SNAPSHOT"
    public static String gitCommit();        // ex: "8d670fb0"
    public static String buildTimestamp();   // ex: "2026-05-09T19:43:04Z"
    public static String javaVersion();      // ex: "25.0.3"
    public static String serverHeader();     // ex: "Chappe/0.1.0-SNAPSHOT+8d670fb0 (2026-05-09T19:43:04Z)"
}

Lu une fois au démarrage depuis META-INF/chappe-build.properties (généré par filtering Maven + git-commit-id-maven-plugin au build de chappe-api). Utilisé automatiquement par la couche HTTP pour injecter les headers Server et X-Chappe-Build.

StaticFileHandler

StaticFileHandler.builder()
    .addPath(Path)                       // filesystem (zero-copy sendfile)
    .addClasspath(String resourceRoot)   // classpath (jar, module)
    .cacheInMemory(boolean)              // cache ETag pour ressources classpath
    .cacheControl(String)
    .preferPrecompressed(boolean)        // sidecars .br / .gz si Accept-Encoding
    .spaFallback(String)                 // 200 sur 404 (mode SPA)
    .notFoundFile(String)                // 404 sur 404 (mutuellement exclusif)
    .indexFiles(String...)
    .build();

AcceptEncoding

record Entry(String coding, double qvalue) {}

List<Entry>  AcceptEncoding.parse(String header);
boolean      AcceptEncoding.accepts(String header, String coding);

Algorithme RFC 9110 §12.5.3 : q-values, wildcard, identity implicite.

MimeTypes

String mime = MimeTypes.detect(path);    // 26+ types : text/html, application/json, image/png, ...

RequestContext

public final class RequestContext {
    public static final ScopedValue<RequestContext> CURRENT;
    public static Request currentRequest();
    public Map<String, Object> attributes();
}

Configuration CLI chappe serve

Flags

Flag Effet

--config FILE

charge un fichier YAML

--root DIR

override static.root

--port N

override server.port (défaut 8080)

--bind ADDR

override server.bind (défaut 0.0.0.0)

--fallback PATH

override static.fallback (status 404)

--spa-fallback PATH

active le mode SPA (status 200 sur 404)

--cache-control STR

override static.cache-control

--gzip / --no-gzip

active/désactive la compression

--access-log / --no-access-log

active/désactive l’access log Apache CLF sur stdout

--header KEY=VALUE

ajoute un header (répétable, merge dans headers.always)

-h, --help

aide

Les flags ont priorité sur le YAML.

Variables d’environnement

Variable Effet

STAGING=true

active le bloc headers.staging (typiquement X-Robots-Tag: noindex, nofollow)

CHAPPE_ACCESS_LOG=true

active l’access log (équivalent à --access-log, le flag CLI override l’env)

Schéma YAML

server:
  port: 8080
  bind: 0.0.0.0

static:
  root: /var/www/site
  fallback: /404.html               # status 404
  spa-fallback: /index.html         # status 200 (mutuellement exclusif avec fallback)
  index-files: [index.html]
  cache-control: "max-age=3600, public"
  gzip: true                         # négocié via Accept-Encoding

headers:
  always:                            # injectés sur chaque réponse
    X-Content-Type-Options: "nosniff"
    Referrer-Policy: "strict-origin-when-cross-origin"
  staging:                           # actif si l'env var STAGING=true
    X-Robots-Tag: "noindex, nofollow"

logging:
  level: INFO
  access-log: false

Subset documenté : maps imbriquées, listes inline [a, b] et bloc - item, scalaires string/int/bool, commentaires #. Pas d’anchors, de tags !!, de multilignes.

Observabilité

Headers de build

Toutes les réponses HTTP sortant de Chappe portent deux headers identifiant précisément le binaire qui les a produites :

Server: Chappe/0.1.0-SNAPSHOT+8d670fb0 (2026-05-09T19:43:04Z)
X-Chappe-Build: Chappe/0.1.0-SNAPSHOT+8d670fb0 (2026-05-09T19:43:04Z)

Format : Chappe/<version Maven>+<short hash Git>(<build timestamp ISO-8601 UTC>). Lu une fois au démarrage via BuildInfo (cf. API publique).

  • Server est le header standard. Il peut être réécrit par un reverse proxy en amont (NPM, openresty, Cloudflare).

  • X-Chappe-Build double l’info dans un header non-standard que les proxies ne réécrivent généralement pas — utile pour identifier le binaire derrière un reverse proxy qui fixe son propre Server.

Idempotence : si l’application a déjà fixé l’un de ces headers dans la Response, Chappe ne l’écrase pas.

Access log

Activable via --access-log (CLI), CHAPPE_ACCESS_LOG=true (env) ou logging.access-log: true (YAML). La CLI prime sur l’env qui prime sur le YAML.

Format : Apache Combined Log Format étendu, sortie sur System.out (capté par Docker/Portainer) :

1.2.3.4 - yann.blazart@gmail.com [09/May/2026:18:50:54 +0000] "GET /a.png HTTP/1.1" 200 877719 12ms

Résolution des champs :

  • IP cliente : X-Forwarded-For (premier hop) → X-Real-IPRequest.remoteAddress()-.

  • User authentifié : X-Forwarded-UserGap-Auth (oauth2-proxy) → -.

  • Size : Response.body().contentLength() (ou - si streamé/chunked).

  • Durée : nanosecondes mesurées par le filter en outermost wrapper, capture le status final même quand le handler lève une exception.

Le filter est exposé en API publique via Filter.accessLog() / Filter.accessLog(Consumer<String>) (cf. ci-dessus) ; le launcher CLI l’applique en outermost.

Plugin Maven : pré-compression

<plugin>
  <groupId>io.vidocq.chappe</groupId>
  <artifactId>chappe-static-index-maven-plugin</artifactId>
  <executions>
    <execution>
      <goals><goal>index</goal></goals>
      <configuration>
        <compress>gzip</compress>            <!-- défaut: none -->
        <compressThreshold>1024</compressThreshold>
      </configuration>
    </execution>
  </executions>
</plugin>

Génère :

  • META-INF/chappe-static-index.properties — index O(1) (chemin → taille + ETag).

  • Sidecars .gz pour les fichiers > seuil.

Servis zero-copy par StaticFileHandler.preferPrecompressed(true).

Capacités HTTP

Spec État Notes

RFC 9110 (HTTP Semantics)

Date, HEAD/204/304, 405+Allow, Host, 100-continue

RFC 9112 (HTTP/1.1)

request parsing, chunked, keep-alive, pipelining

RFC 9113 (HTTP/2)

framing, flow control, lifecycle, GOAWAY, SETTINGS, CONTINUATION

RFC 7541 (HPACK)

table statique, table dynamique, Huffman encode/decode

RFC 9114 (HTTP/3)

planifié — voir lien:https://codeberg.org/Vidocq/chappe/blob/main/BUG.md[BUG.md]

RFC 8441 (WebSocket H2)

planifié

Compatibilité

  • Java : 25 (LTS), aucun flag preview requis (ScopedValue est final depuis JEP 506).

  • Maven : 3.9.16 (POM modelVersion 4.0.0).

  • JPMS strict : aucun classpath, aucun --add-opens.

Bugs et benchmarks

  • lien:https://codeberg.org/Vidocq/chappe/blob/main/BUG.md[BUG.md] — bugs reproductibles, statut, repro minimal.

  • lien:https://codeberg.org/Vidocq/chappe/blob/main/BENCH.md[BENCH.md] — chiffres de performance, hardware, JVM, commande.

Source

Tout le code et la roadmap sont sur https://codeberg.org/Vidocq/chappe.