Before diving into internals, let’s pin down the vocabulary. The terms below come from the Jakarta Servlet 6.1 spec as implemented by Foy.

Core vocabulary

Term Meaning in Foy

Request / Response

HttpServletRequest / HttpServletResponse. Implemented by HttpServletRequestImpl / HttpServletResponseImpl, which adapt Chappe's Request / Response with no body copy.

Servlet

A jakarta.servlet.Servlet (typically an HttpServlet) responding to a URL mapping. Discovered through @WebServlet or <servlet> in web.xml.

Filter chain

Pipeline of jakarta.servlet.Filter traversed before the target Servlet. Built by FilterRegistry from <filter-mapping> and @WebFilter. Deterministic order (see Reference).

Listener

A bean reacting to container lifecycle (ServletContextListener), session lifecycle (HttpSessionListener), or request lifecycle (ServletRequestListener). Enrolled by ListenerRegistry from @WebListener.

ServletContext

Implemented by VidocqServletContext. One per contextPath. Carries shared attributes, exposed CDI beans, `RequestDispatcher`s.

RequestDispatcher

forward / include mechanism. Implemented by RequestDispatcherImpl, reuses Chappe routing.

Async

request.startAsync() returns an AsyncContext. No platform pool to protect: each request runs on a virtual thread; async only decouples response from request on the program side.

Multipart

@MultipartConfig + request.getParts(). Parsed by MultipartParser, fully buffered in memory (no streaming parse, no temp-file spill).

Metaphor: the layer above the transport

Chappe moves bytes: it speaks HTTP/1.1 or HTTP/2 over TLS, manages a connection, reads a body. It does not know what a "Servlet" or a "filter" is.

Foy interprets those bytes: it turns an HTTP connection into an HttpServletRequest, finds the Servlet matching the URL, walks the filter chain, writes the response. It is exactly the division the 19th-century French telegraph administration drew between stationnaires (relaying the Chappe code without understanding it) and bureau directors (interpreting the message).

Application discovery

foy-core orchestrates the merge between annotations and the descriptor:

  1. reading of annotations (@WebServlet, @WebFilter, @WebListener, @MultipartConfig) — resolved at compile time by an APT, exposed through the Vauban BeanManager; WebAppDiscovery performs that aggregation at FoyChappeBoot startup;

  2. reading of the descriptor (WEB-INF/web.xml, supported subset — see Reference) via WebXmlParser, producing a WebAppDescriptor; WebXmlContributor contributes it after annotation discovery, never overriding a name already discovered.

META-INF/web-fragment.xml scanning and the metadata-complete attribute are not implemented yet — the merge is purely additive. This is the top known gap (81 % of the current TCK failures) — see TCK status.

No runtime classpath scan. All Servlet beans are already known to Vauban at compile time.

Chappe ↔ Servlet bridge

foy-core hosts ChappeServletBridge (package io.vidocq.foy.internal.bridge; foy-chappe’s `FoyChappeBoot builds it), a Chappe Handler that:

  1. exposes the Chappe Request as HttpServletRequestImpl;

  2. exposes the Chappe Response as HttpServletResponseImpl;

  3. invokes the Filter → Servlet chain;

  4. returns the Chappe Body built around ServletOutputStreamImpl.

The Servlet OutputStream is wired directly to Chappe streaming — no body copy, no intermediate buffer.

Application security

  • BasicAuthenticator — standard HTTP Basic (RFC 7617).

  • SecurityConstraintEnforcer — applies @ServletSecurity constraints (@HttpConstraint, @HttpMethodConstraint) read from the servlet class. <security-constraint> in web.xml is not parsed.

  • AnonymousSecurityProvider — open fallback for development.

  • Public SPI: io.vidocq.foy.spi.security.SecurityProvider for plugging custom providers (LDAP, JWT, OIDC).

Form-based and Digest authentication are not implemented (the spec.security.* TCK family passes 21/59). Jakarta Authentication / Authorization: not implemented, later milestone.

CDI: a single BeanManager

foy-cdi-vauban connects Foy to Vauban through a single class: FoyVaubanBootstrap, which returns the current VaubanContainer’s `BeanManager for FoyChappeBoot.

  • Servlet/Filter/Listener instances are resolved through the BeanManager, so they can carry @Inject for application beans.

  • ServletContext, HttpServletRequest and HttpSession are not exposed as CDI beans yet — no @ApplicationScoped/@RequestScoped/@SessionScoped producers exist for them.

Resolution is fully build-time through Vauban — no dynamic proxy, no hot-path reflection, AOT-friendly (GraalVM, Leyden CDS).

Servlet 6.1 vs 5.0 differences

Aspect Servlet 5.0 (Jakarta EE 9.1) Servlet 6.1 (Jakarta EE 11)

Package

jakarta.servlet.* (rebrand)

jakarta.servlet.* (stable)

Java modules

Optional

Recommended — Foy enforces it

I/O threading

Platform pool + NIO Selector

Virtual threads (Foy: one VT per request)

Legacy API

SingleThreadModel, partial <run-as>

Removed / clarified

WebSocket

Separate spec 2.1

Separate spec 2.2 — not implemented in Foy (planned, no date)

HTTP/3

Out of scope

Out of scope (transport — see Chappe)

Foy focuses on the relevant 2026 subset: no JSP, no runtime SCI through META-INF/services, no reflection. Libraries that depended on those features need to be ported through an explicit SPI or an APT.