🔌

wo-adaptor-jetty

A Jetty-based WOAdaptor

Version
v0.12.3
Updated
Oct 18

0.12.3

v0.12.3 Oct 18 on GitHub ↗

Bugfix release, with a security fix.

  • Request header names are merged case-insensitively. Fields whose names differed only in case, such as remote_addr and REMOTE_ADDR, reached WebObjects as one header with only one of the values, chosen by hash-map iteration order. Behind Apache, where mod_WebObjects adds REMOTE_ADDR after copying the client's headers, a client could thereby choose the address the application saw, and get past an IP allow-list. Every value of a header now arrives under one name, in the order the fields came, so an application can see that an address header has more than one value and refuse it. (#17)
  • Jetty 12.1.14, up from 12.1.13.

Upgrading from 0.12.2

No changes needed. Applications that make decisions based on the client address should also refuse an address header carrying more than one value; wonder-slim does this as of undur/wonder-slim#219.

Full changelog: CHANGES.md

0.12.2

v0.12.2 Oct 1 on GitHub ↗

Bugfix release.

  • Request paths may contain an encoded slash. A path segment with %2F in it, such as a file name containing Etc/GMT, was refused with 400 Ambiguous URI path separator before the request reached WebObjects. The path now reaches WO still encoded, so WO decodes the segment and the path's structure is kept. Encoded . and .. segments are still refused. (#9)
  • The WebSocket upgrade handler starts the chain it wraps. With the push module on the classpath and JettyMaxConcurrentRequests set, every request failed with a 500, because the QoS handler inside the upgrade handler was never started. (#10)
  • A numeric property that isn't a number stops the adaptor at startup, naming the property and the value. Previously a typo such as -DJettyMaxConcurrentRequests=5O was read as 0, silently leaving the concurrency limit, a timeout or the accept queue size at its default. (#11)
  • Each response header value is sent as a field of its own, instead of values appended more than once being joined with commas, which mangled non-list headers and made WWW-Authenticate ambiguous. (#12)
  • WebSocket fixes in the experimental support: a binary message no longer stops the session; onBinaryMessage gets a copy of the payload it may keep; an unset JettyWebSocketIdleTimeout now means Jetty's default of 30 seconds rather than none; the adaptor stops a handler's heartbeat after onClose. (#13)
  • Smaller fixes: stopping an adaptor whose startup failed early no longer throws, a failure to start the server is logged before exiting, response content is written without a copy, and documentation corrections. (#14)
  • A test suite, running the adaptor in front of a real WOApplication and asserting on the raw HTTP. (#15)

Upgrading from 0.12.1

  • A numeric Jetty* property or WOListenQueueSize set to something that isn't a number now stops startup instead of being ignored. Check your launch arguments if an instance fails to start after upgrading.
  • WebSocket connections now time out after 30 seconds idle unless JettyWebSocketIdleTimeout is set. Set it to 0 for the previous behaviour, or send a heartbeat more often than the timeout.

Applications building their own server through JettyServerProvider set URI compliance on their own HttpConfiguration.

Full changelog: CHANGES.md

0.12.1

v0.12.1 Sep 23 on GitHub ↗

Bugfix release.

  • Request paths may contain characters such as |. Jetty's default URI compliance rejected them with 400 Illegal Path Character before the request ever reached WebObjects, so such URLs failed with nothing in the application's log. The classic adaptor passes these paths through, and URLs in the wild carry them. The adaptor now allows Jetty's ILLEGAL_PATH_CHARACTERS violation, which also admits raw " < > { } ^ and backtick in a path, matching the classic adaptor's behaviour; as with the classic adaptor, such characters reach the application unencoded.

Still rejected: suspicious characters (backslash, NUL, control characters), encoded slashes (see #9) and path traversal.

Upgrading from 0.12.0 needs no changes. Applies to the adaptor's default server; applications building their own through JettyServerProvider set the compliance on their own HttpConfiguration.

0.12.0

v0.12.0 Sep 19 on GitHub ↗

The adaptor now selects itself. An application that depends on wo-adaptor-jetty no longer needs -WOAdaptor WOAdaptorJetty anywhere: having it on the classpath is taken to mean you want to use it.

The core module is a WO framework whose principal class, loaded by NSBundle while bundles are being initialized, names the adaptor. It sets the WOAdaptor property rather than the static field WO resolves it into, and that distinction is what keeps you in control: at bundle-loading time the properties files have been read but the launch arguments have not, so WO overwrites the property when it parses them a moment later. An explicit -WOAdaptor, or the property set in a properties file, therefore still wins — including -WOAdaptor WODefaultAdaptor to go back to the classic adaptor.

Verified against a real application in all three shapes: nothing set selects Jetty, -WOAdaptor WODefaultAdaptor gets the classic adaptor, -WOAdaptor WOAdaptorJetty gets Jetty.

Upgrading needs no changes. Existing applications passing the launch argument keep working and can drop it; a global WOAdaptor entry in ~/WebObjects.properties can go too, which is worth doing, since it otherwise applies to every application on the machine whether or not it has this adaptor.

0.11.0

v0.11.0 Sep 19 on GitHub ↗

Two fixes to SSEStream, both found by running server-sent events in JavaMonitor against a quiet production log. Either one alone leaves a quiet feed broken behind a proxy, so an application consuming this version needs no workarounds of its own.

  • The response is committed as soon as the stream is created. A stream now writes an opening comment on construction. Previously nothing was written until the first event or the first keep-alive, so the adaptor had not committed the response and no headers reached the client: the browser sat in "connecting", and every proxy in between saw an idle connection it might decide to time out.

  • The default keep-alive interval is 10 seconds, down from 30. A WebObjects instance's adaptor configuration carries a recvTimeout that JavaMonitor defaults to 30 seconds, and modulo applies it as the idle timeout of its connection to the instance. A keep-alive interval equal to that timeout is a race the timeout can win; the client answers an aborted stream by reconnecting, and a quiet feed loops like that forever while a busy one looks perfectly healthy. Ten seconds clears the 30-second default with room for scheduling jitter, at a cost of a few bytes per stream per interval.

Upgrading from 0.10.0 needs no code changes, and applications that worked around either problem — sending a comment on subscribe, or setting a shorter interval explicitly — can drop those overrides.

0.10.0

v0.10.0 Sep 17 on GitHub ↗

Server push, and the streaming responses it needs. In production use; still 0.x, so the API may move before it settles.

  • Server-sent events: SSEStream and SSEHub in wo-adaptor-jetty-push (return an open-ended WOResponse from any action and keep sending to it)
  • Responses with a content stream of unknown length are streamed with chunked transfer encoding until the stream ends, instead of being sent with a Content-Length of 0 — this is what makes SSE and other open-ended responses work
  • JettyQoSExcludedPaths: path specs that bypass the QoS concurrency limit, for long-lived responses that would otherwise hold a permit for their lifetime
  • SSE responses send Cache-Control: no-store; the Firefox request-coalescing gotcha for repeated stream URLs is documented in SSEStream

Dependency change: the WebSocket module wo-adaptor-jetty-websocket is renamed wo-adaptor-jetty-push, since it now holds server push in general. Apps using WebSockets update the artifactId. Artifacts are on the WOCommunity maven repository under is.rebbi; requires JDK 25.

0.9.0

v0.9.0 Sep 15 on GitHub ↗

First release. In production use since November 2025; published as a pre-release ahead of 1.0 while the package name and configuration surface are finalised.

  • Two modules sharing one version: wo-adaptor-jetty (the adaptor) and wo-adaptor-jetty-websocket (experimental WebSocket support, enabled by its presence on the classpath through the JettyHandlerDecorator extension point)
  • Optional request backpressure via a Jetty QoSHandler: JettyMaxConcurrentRequests, JettyMaxSuspendedRequests, JettyMaxSuspendSeconds
  • Connector idle timeout (JettyConnectorIdleTimeoutSeconds, default 600) and accept queue size honoring WO's WOListenQueueSize (default 511)
  • Request bodies sent without a Content-Length are answered with 411 Length Required instead of being silently treated as empty
  • Repeated same-name request headers are no longer dropped
  • Port discovery for WOPort 0 uses the first network connector and fails clearly if there is none
  • WOAdaptorJetty.UNHANDLED_RESPONSE_KEY: a documented contract for letting a request fall through to the next Jetty handler
  • JettyServerProvider lets an application build the Jetty server itself

Artifacts are on the WOCommunity maven repository under is.rebbi. Requires JDK 25.