Skip to content

chore(deps): update dependency org.mock-server:mockserver-netty-no-dependencies to v9.2.0 - #2613

Merged
renovate[bot] merged 1 commit into
mainfrom
renovate/org.mock-server-mockserver-netty-no-dependencies-9.x
Oct 27, 2026
Merged

renovate[bot] merged 1 commit into
mainfrom
renovate/org.mock-server-mockserver-netty-no-dependencies-9.x

Conversation

@renovate

@renovate renovate Bot commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
org.mock-server:mockserver-netty-no-dependencies (source) 9.1.0 → 9.2.0 age confidence

Release Notes

mock-server/mockserver-monorepo (org.mock-server:mockserver-netty-no-dependencies)

v9.2.0

Security
  • In the mockserver-netty-no-dependencies jar and the Docker images, the bundled Kafka client used for AsyncAPI mocking now refuses the JndiLoginModule and LdapLoginModule JAAS login modules in a saslJaasConfig, as Kafka does by default since CVE-2023-25194. The jar renames the libraries it bundles, and the renaming changed the module names Kafka checks against, so the check never matched (#​2869)
  • With dataPlaneAuthenticationRequired on, a mocked endpoint could still be reached without credentials in several ways. A path that only starts with /mockserver (such as /mockserverfoo), or a /mockserver/... path that the server does not handle (such as /mockserver/nope, or /mockserver/status and /ready over HTTP/3), skipped the check and was answered by any matching expectation, a catch-all one included. Over HTTP/1.1, an expectation with respondBeforeBody answered the first request on a connection without checking credentials. And a gRPC bidirectional-streaming expectation (respondWithGrpcBidi) answered over HTTP/2 or HTTP/3 without checking them. gRPC server reflection also answered without credentials, listing the loaded services and message types, and so did a gRPC chaos fault and the UNIMPLEMENTED answer for an unknown gRPC method, which told the caller which methods exist. All of these are now refused (401, or gRPC status UNAUTHENTICATED) like any other mocked endpoint. A refused request does not use up the expectation's Times, move its scenario on or count towards a gRPC chaos profile. The control plane, the readiness and liveness probes (the gRPC health check included), and CORS preflights for the control plane still work without data-plane credentials. Data-plane authentication does not apply to DNS mocking
  • dataPlaneAuthenticationRequired now applies to the WAR deployments (mockserver-war, mockserver-proxy-war) and to InProcessMockServer, which served their mocks without credentials whatever it said. Credentials are checked before a request can match an expectation, so a refused request uses up no Times and moves no scenario, and a CORS preflight for a control-plane route stays open, as on the standalone server. An in-process request carries its credentials in its headers. In a WAR, tlsMutualAuthenticationRequired is now enforced too: a mocked request over plain HTTP gets 426 Upgrade Required, and one over HTTPS without a client certificate verified by the servlet container gets 401; the container's trust store decides which certificates are accepted (see upgrading to 9).
  • The Kafka SASL JAAS configuration for AsyncAPI broker mocking can now only be set at startup, with the new asyncKafkaSaslJaasConfig property (-Dmockserver.asyncKafkaSaslJaasConfig or MOCKSERVER_ASYNC_KAFKA_SASL_JAAS_CONFIG). A JAAS configuration names the Java login class the Kafka client loads, and PUT /mockserver/asyncapi let any control-plane caller choose it through brokerConfig.kafkaSecurity.saslJaasConfig. That request is now rejected with a 400 that names the startup property, and so is any other field in kafkaSecurity that MockServer does not know, so no Kafka client setting that loads a class can be passed in; saslMechanism and securityProtocol must be one of Kafka's own values. A request that selects SASL_PLAINTEXT or SASL_SSL uses the startup JAAS configuration (or the JVM's own JAAS file). Because every SASL login is now held by the server, such a request may only use the startup asyncKafkaBootstrapServers: it must omit kafkaBootstrapServers or name the same brokers, or it gets a 400 and no credential is sent, and it gets a 400 while no startup bootstrap servers are set. asyncKafkaBootstrapServers can now only be set at startup: PUT /mockserver/configuration ignores it and logs a warning. PUT /mockserver/configuration ignores asyncKafkaSaslJaasConfig and logs a warning, and GET /mockserver/configuration never returns it. To upgrade, move the saslJaasConfig value from your requests to asyncKafkaSaslJaasConfig, set asyncKafkaBootstrapServers at startup to the brokers it logs in to, and keep securityProtocol and saslMechanism in the request (see upgrading to 9)
  • retrieve with forwardUnmatchedTo now needs a control-plane role that may change state. The parameter repoints where unmatched requests are forwarded, but control-plane authorization treated the call as a read, so with controlPlaneAuthorizationEnabled a principal with only the READ role could redirect unmatched traffic. Such a principal now gets 403, as it already did for extractBodiesLargerThan; a retrieve without either parameter is still a read (see upgrading to 9)
  • Upgraded DOMPurify (dashboard UI dependency) to 3.4.17 to fix GHSA-cg2g-xh64-v4q7
  • Build-time dependency fixes, none shipped in a published package: the Node Testcontainers module's own development lockfile resolves undici 7.30.1 (Dependabot alerts 610, 611, 612; the published package ships only dist), and the VS Code extension's development dependency @vscode/vsce now resolves @azure/msal-node 7.1 through an npm overrides entry (alert 613; it reaches the build only through @vscode/vsce → @azure/identity, which has no release that allows msal-node 7 yet)
  • The paths MockServer writes files to, and the protoc executable it runs, can now only be set at startup: auditLogFile, persistedExpectationsPath, persistedRecordedExpectationsPath, persistedRecordedRequestsPath, memoryUsageCsvDirectory and directoryToSaveDynamicSSLCertificate, like recordedBodyFilesDirectory. Before, PUT /mockserver/configuration (or a client's updateConfiguration) could change them on a running server, so anyone who could reach the control plane could redirect the audit log (before its first write) or the generated certificates and their private keys into another directory, such as one on the extension classpath, or point a format=recording disk import (which reads persistedRecordedRequestsPath) at another file. grpcProtocPath, which names the protoc executable MockServer runs, is now startup-only for the same reason. A PUT that sets a different value is still answered, but the value is ignored and a warning is logged; a PUT that sends the value already in force logs nothing. Set them with a system property, environment variable, properties file or Configuration instead (see upgrading to 9)
  • The settings that decide who may use the control plane, what each user may do, whether control-plane changes are audited, and where dashboard usage events go can now only be set at startup: controlPlaneTLSMutualAuthenticationRequired, controlPlaneTLSMutualAuthenticationCAChain, the controlPlaneJWTAuthentication* and controlPlaneOidc* settings, controlPlaneAuthorizationEnabled, controlPlaneScopeMapping, controlPlaneAuditEnabled and dashboardAnalyticsEndpoint, like the controlPlaneBasicAuthentication* settings. Before, anyone allowed to change the configuration with PUT /mockserver/configuration could switch control-plane authentication off, trust their own keys or identity provider, give themselves a role, switch auditing off before making changes, or send every dashboard viewer's usage events to their own server. A PUT that sets a different value is still answered, but the value is ignored and a warning is logged, and GET /mockserver/configuration reports the value in force; the dashboard's Audit Trail view no longer has an on/off switch (see upgrading to 9)
  • A PUT /mockserver/configuration that changes where the default LLM backend sends requests (llmBaseUrl, or llmProvider while llmBaseUrl is empty) without also supplying llmApiKey now clears the held key and logs a warning, so the key is never sent to the new endpoint. Before, anyone allowed to change the configuration could collect the key by pointing llmBaseUrl at their own server. A PUT that supplies both applies both; a JSON llmBackendsConfig value that changes a backend's baseUrl or provider while leaving its key masked is not applied (see upgrading to 9)
  • A held secret is no longer sent to a destination that a control-plane caller can choose. clusterVerifyFanInPeers and clusterFanInPeerAuthToken can now only be set at startup: PUT /mockserver/configuration could repoint the fan-in peers at any host, and every verify or retrieve then sent it the cluster peer token, which grants write access to every node. A PUT that supplies a different value is ignored and logs a warning. The detect_llm_drift MCP tool, which a read-only caller may use, now refuses a cassette whose provider is not the configured LLM backend's provider and sends nothing; before, it replayed the cassette with that provider's client, which, when no llmBaseUrl was set, sent the held llmApiKey to that provider's default host. An AZURE_OPENAI backend with no llmBaseUrl now sends nothing, instead of sending its key to the placeholder host example-resource.openai.azure.com. See upgrading to 9.
  • noProxyHosts can now only be set at startup, like the upstream proxies it applies to (forwardHttpProxy, forwardHttpsProxy, forwardSocksProxy). Before, anyone allowed to change the configuration with PUT /mockserver/configuration could add hosts to it and so route forwarded traffic around an upstream proxy relied on for logging or data-loss checks. A PUT that sets a different value is still answered, but the value is ignored and a warning is logged, and GET /mockserver/configuration reports the value in force (see upgrading to 9)
  • Control-plane credentials no longer reach the event log. A successful control-plane call (for example changing the clock, the configuration, a scenario or a chaos experiment) used to store its whole request, Authorization header included, in the log, where anyone allowed to read logs (PUT /mockserver/retrieve?type=LOGS, including a read-only principal when control-plane authorization is on) and the dashboard could see it. Bearer tokens (JWT and OIDC) were exposed this way before this release. Every request the control-plane authentication gate sees is now logged with its Authorization, Proxy-Authorization, cookie and API-key headers masked, whatever redactSecretsInLog says; authentication and authorization failure entries are masked the same way. Mocked (data-plane) requests are still logged exactly as they were sent, so verification by header is unchanged; the diagnostic entry for a cut-short or undecodable request masks credentials whatever its path. TRACE-level wire logging still prints raw traffic, headers included, to the console or log file (not to the event log), so keep TRACE logs private. If your control plane uses bearer tokens and others could read its logs, treat the tokens it logged as exposed
Added
  • Error actions: "resetConnection": true answers a matched request with a TCP reset instead of a response, so the client sees "connection reset by peer" (ECONNRESET), where dropConnection closes the connection cleanly (Java client: error().withResetConnection(true); also in the Kotlin DSL, every client library and the dashboard composer). Over TLS no TLS close message is sent first. Over HTTP/2 the whole connection is reset (use streamError to reset one request); over HTTP/3, which has no TCP reset, the QUIC connection is closed with H3_INTERNAL_ERROR. Through a CONNECT or SOCKS tunnel the client's own connection is reset. WireMock and Mountebank CONNECTION_RESET_BY_PEER faults now import as resetConnection. Not available in the WAR deployment, like every error action. See error actions
  • Response templates: a systemValue helper reads an environment variable or a system property, like WireMock's systemValue: Velocity $systemValue.env('APP_REGION') and $systemValue.property('app.version'), JavaScript systemValue.env('APP_REGION'), Mustache {{#systemValue.env}}APP_REGION{{/systemValue.env}} and {{#systemValue.property}}app.version{{/systemValue.property}}. It only reads names listed in the new startup-only templateSystemValueAllowList property, like WireMock's --permitted-system-keys: a comma-separated list of exact names or prefixes ending in *, such as APP_*,HOSTNAME. The list is empty by default, so systemValue returns an empty value for every name until you set it. A name that is not on the list also returns an empty value, the same as a name that is not set, and is logged as a warning once. A bare * is ignored, and there is no way to list names or values. One list covers both environment variables and system properties, and MockServer's own settings (names starting with mockserver. or MOCKSERVER_, in any case) and the javax.net.ssl key and trust store passwords are never readable, whatever the list says; an entry that covers them is reported when MockServer starts, as is what templates can read. PUT /mockserver/configuration cannot change the list and logs a warning if asked to. Anyone who can create expectations, call them or read the log can read the values of the names you list, so list only values that are safe to share. Templates that do not use systemValue run as before. See templateSystemValueAllowList
  • Kotlin DSL, like WireMock's kotlin-wiremock: the new org.mock-server:mockserver-client-kotlin artifact (also in mockserver-bom) adds Kotlin builders and extension functions to MockServerClient and ClientAndServer, for example mockServer.expect { request { method = "GET"; path = "/x" } respond { status = 200; body = "ok" } }. An expectation can respond, forward or error, with times, timeToLive, priority and id; requests match on method, path, headers, query and path parameters, cookies, an exact, JSON or any Java body matcher, and customize { } reaches every other Java builder method. verify(times) { } and verifySequence { } verify requests, and clear { }, retrieveRecordedRequests { } and retrieveActiveExpectations { } take the same request block; standalone request { }, response { } and expectation { } builders return Java objects for any other client method. It brings in kotlin-stdlib 2.4.20 and changes nothing for Java users. See Kotlin DSL
  • In-process mode with no HTTP server, like WireMock's DirectCallHttpServer: new InProcessMockServer() (in mockserver-core) handles requests as Java calls, for hosts such as AWS Lambda or a test of an HTTP adapter. handle(HttpRequest) returns the HttpResponse, and handleAsync(HttpRequest) returns a CompletableFuture. Requests go through the same matching and actions as a running MockServer, so expectations, templates, delays, forwards (through MockServer's own HTTP client and any configured forwardHttpProxy, forwardHttpsProxy or forwardSocksProxy), class callbacks, verification and the log behave the same. Most of the REST control plane works through handle too, with control-plane authentication as configured; routes only the Netty server serves (/mockserver/configuration, /mockserver/ready, /mockserver/metrics, the dashboard) answer 404. upsert, verify, retrieveRecordedRequests, reset and getHttpState() give Java access to the control plane; they are in-JVM calls and are not authenticated, so if handle receives untrusted traffic, enable control-plane authentication or drop /mockserver/ paths first. handle blocks the caller for a response delay or a forward; handleAsync does not. An unmatched request is answered 404 and never proxied to the host in its Host header. Object callbacks (which need a WebSocket; use a class callback), SSE, WebSocket and gRPC streaming, and connection faults are not available. close() stops every thread it started (a delayed response still pending completes when its delay elapses), and every method then throws IllegalStateException. The Netty server is unchanged. See running in-process
  • Raw network traffic logging, like WireMock's --print-all-network-traffic, for debugging only: start MockServer with logRawNetworkTraffic=true to log every read and write on each connection to MockServer, and on the HTTP requests it sends itself (forwards, proxied requests, webhooks), at INFO on the org.mockserver.logging.RawNetworkTrafficLog logger. HTTP/1.1 is shown as text after TLS decryption, HTTP/2 as decoded frames and other protocols as a hex dump, at most 4,096 bytes per read or write, with line breaks and control characters escaped so traffic cannot forge log lines. Authorization, Proxy-Authorization, cookies, API-key and other credential headers are masked, but bodies, query strings and hex dumps (including their text column) are logged as sent and may contain secrets, so do not use it in production. It can only be set at startup (PUT /mockserver/configuration ignores it with a warning), and when it is off nothing is added to a connection. Not logged: HTTP/3, the upstream connections of proxied WebSockets and relayed binary connections, requests between cluster members, and metrics export.
  • Date-time request matchers, like WireMock's before, after and equalToDateTime. Give a header, cookie, query-string or path-parameter value as an object such as {"after": "now -1 hours"} (Java client: withHeader(string("X-Issued-At"), NottableDateTimeString.after("now -1 hours"))) and the request value is compared as a date-time. The expected value is now, now with an offset (now -1 hours, now +3 days, now PT30M) or an ISO-8601, RFC-1123 or epoch date-time. Optional fields set the request value's format (actualFormat: a pattern such as dd/MM/yyyy, or iso, rfc1123, epochSeconds, epochMillis), truncate either side (truncateExpected, truncateActual, such as first day of month or startOfDay), truncate now after its offset (applyTruncationLast), set the time zone used for values without one, truncation and calendar offsets (zone, default UTC) and negate the match (not). now follows the controllable clock. Parsing is strict, so 30 February never matches, and a request value that is not a date-time never matches, even when negated. An invalid matcher is rejected when the expectation is created, with the reason. Plain string values are unchanged, so a value that happens to read before:2026-01-01 still matches that text, and expectations without date-time matchers do no extra work. Every client library models the object form and round-trips it: Go mockserver.After("now -1 hours") with HeaderMatcher and the other *Matcher builders; Rust MatcherValue::after(..) with header_matcher and the others; .NET MatcherValue.After(..); Python DateTimeMatcher.after(..); Ruby MockServer::DateTimeMatcher.after(..); PHP DateTimeMatcher::after(..) with the new headerMatcher, queryStringParameterMatcher, cookieMatcher and pathParameter builders; and Node typings (DateTimeMatcher). Each has with... options matching Java's, and plain string values are sent unchanged. The dashboard composer can edit a date-time value, keeps any object-form value it does not edit, and its code tabs generate these builders. The WireMock importer now maps before, after and equalToDateTime header and query predicates (with expectedOffset, actualFormat and truncation) onto them. A failed expectation parse now also says why. See date-time matching
  • Date-time matching inside JSON and XML bodies, like WireMock's matchesJsonPath and matchesXPath with before, after or equalToDateTime. A JSON_PATH or XPATH body matcher takes an optional dateTime field holding the same date-time matcher as a header value, for example {"type": "JSON_PATH", "jsonPath": "$.order.created", "dateTime": {"after": "now -1 days"}} (Java client: jsonPath("$.order.created", after("now -1 days")) or xpath("/order/@expires", after("now"))). The body then matches when a value the JSONPath selects, or the text of a node the XPath selects, is a matching date-time; when the expression selects several, one is enough. A JSON string is read as written and a whole number as epoch seconds or millis; an XPath must select nodes (elements or attributes). A body without the value, or whose value is not a date-time, does not match, even when the comparison is negated. JSONPath and XPath bodies without dateTime run exactly as before, with no extra allocation. Every client library supports it: Go JSONPathBody(..).WithDateTime(mockserver.After(..)), Rust Body::json_path(..).with_date_time(MatcherValue::after(..)), .NET Body.OfJsonPath(..).WithDateTime(MatcherValue.After(..)), Python Body.json_path(.., DateTimeMatcher.after(..)) and XPathBody(date_time=..), Ruby Body.json_path(.., date_time: ..), PHP HttpRequest::jsonPathBody(.., DateTimeMatcher::after(..)), the new xpathBody and matcherBody, and Node typings (JsonPathBodyMatcher, XPathBodyMatcher). The dashboard composer has a Date-Time Comparison field for JSON Path and XPath bodies, and its code tabs generate these builders. The WireMock importer now maps matchesJsonPath and matchesXPath with a before, after or equalToDateTime result matcher onto them, instead of dropping the comparison with a warning. See date-time matching in a body
  • Webhooks: a before- or after-action can now render its request with a Velocity, Mustache or JavaScript template, using httpRequestTemplate in place of httpRequest (Java client: afterAction().withHttpRequestTemplate(template(...)); also modelled by the .NET (AfterAction.HttpRequestTemplate), Go (AfterAction.HttpRequestTemplate), Node (AfterAction.httpRequestTemplate type), Python (AfterAction(http_request_template=...)), Ruby (AfterAction.new(http_request_template: ...)) and Rust (ExpectationAction::request_template) clients, kept verbatim by the PHP client, and offered as a Request template option for before/after actions in the dashboard composer and its code tabs). The template sees the triggering request with the same variables and helpers as response templates and must render a whole request; the rendered destination is checked by forwardProxyBlockPrivateNetworks when it is enabled (it is off by default), and a template that fails is logged and not sent (a blocking FAIL_FAST before-action returns 502). Escape request values in the template (Velocity $esc.java / $esc.url) and keep them out of the destination, or a caller can redirect the webhook. Webhooks using httpRequest and {$request...} runtime expressions are unchanged.
  • Response templates: the dates helper now works on any date and in any time zone, not only on the current time in UTC. dates.parse reads ISO-8601, RFC-1123, epoch seconds or millis, or text in a given pattern; dates.format(date, pattern, zone) formats any date; dates.offset moves a date by amounts such as "-2 hours" or an ISO-8601 period such as P1M; dates.truncate (and methods such as startOfDay(), firstDayOfMonth(), lastDayOfMonth() and firstDayOfNextYear()) moves it to the start of a period in its own time zone; dates.now(zone) gives the current time in a zone; and dates.rfc1123() writes the HTTP date format for headers such as Expires and Last-Modified. The returned dates can be chained, for example $dates.parse($!request.headers['Date'][0]).plusHours(1).rfc1123(). Mustache, which cannot pass arguments, gets the no-argument forms ({{ dates.now.firstDayOfNextMonth.rfc1123 }}) and a {{#dates.render}}date|offset=1 days|format=yyyy-MM-dd{{/dates.render}} section. The current time still comes from the server clock, so clock control applies, and the existing dates methods return exactly what they did before. An invalid date, pattern, time zone, offset or truncation fails the template with an error naming the value
  • A FILE response body can now pick its file from the request, like WireMock's templated bodyFileName. Set "templatedFilePath": true with a templateType of MUSTACHE or VELOCITY, and filePath is rendered against the request before the file is read, for example "filePath": "responses/{{ request.pathParameters.name.0 }}.json". The request-supplied part of the path must stay inside the directory named by the fixed part of filePath before the first template expression, and that fixed part must name a directory (not just /, ./ or a drive). A .. or . segment, a leading or doubled /, \, :, %, a control character such as a null byte, or a symbolic link out of that directory is refused with the same 500 as a file that cannot be read. This is opt-in: without templatedFilePath, or when the path has no template syntax, the path is used exactly as before. Every client library supports it: Java FileBody.withTemplatedFilePath(true), Node typings templatedFilePath, Python Body.file(..., templated_file_path=True), Ruby Body.file(..., templated_file_path: true), Go FileBody.TemplatedFilePath and ResponseBuilder.BodyFromTemplatedFile, .NET FileBody.TemplatedFilePath and WithTemplatedFileBody, Rust Body::with_templated_file_path(true) and PHP fileBody(..., true); each omits the field when it is false, so existing expectations serialise unchanged. The dashboard composer has a matching option on a FILE body, and its generated code carries the flag in every language
  • Response templates: the request model in Velocity, Mustache and JavaScript now has formData (the fields of an application/x-www-form-urlencoded body), parts (the parts of a multipart/form-data body, each with name, filename, contentType, headers, body and bodyAsBase64), host, port, scheme and baseUrl (from the Host header or HTTP/2 and HTTP/3 :authority, else the local address), bodyAsBase64, and id (the request's log correlation id). Each is worked out only when a template reads it, so other templates and non-templated responses cost nothing extra; a part's body and bodyAsBase64 are built only when read, so reading an upload's filename does not copy its content. Form and multipart decoding is bounded (1,000 fields; 100 parts; 10 MiB, separate from maxRequestBodySize), hitting a bound logs a warning that a template dry run reports, and a malformed body gives an empty map rather than an error. host, port and baseUrl come from the client-set Host header; X-Forwarded-* headers are not used. In JavaScript these fields are not enumerated, so Object.keys(request) and JSON.stringify(request) are unchanged.
  • Recording: three new options on retrieving recorded expectations (PUT /mockserver/retrieve?type=RECORDED_EXPECTATIONS), matching WireMock's recording options. repeatsAsScenarios=true turns a request that was recorded several times with different responses into one expectation that replays those responses in the recorded order and then keeps returning the last one; recordings count as the same request only when every header and other matched part is equal, and a request that got the same response every time is left as it was. captureHeaders=X-Tenant,Accept keeps only the named request headers in each recorded expectation (an empty value keeps none). extractBodiesLargerThan=<bytes> writes each larger response body to a file and returns a FILE body that points at it; files are written only to the directory set by the new recordedBodyFilesDirectory property (empty by default, which disables this and makes such a retrieve a 400; it can only be set at startup), are named after a SHA-256 of their contents, never after anything in the request, are never deleted by MockServer, and are capped at 1,000 files, 256 MB and maxFutureTimeoutInMillis of writing per retrieve. With control-plane authorization on, a retrieve that writes files needs the MUTATE role. Extracted bodies are written exactly as recorded, secrets included (redactSecretsInRecordedExpectations does not redact bodies), so use a private directory and review its files before sharing them. Each option is off unless given, and only changes the returned expectations, not the recorded traffic. The Java client takes them as retrieveRecordedExpectations(request, recordedExpectationsOptions().withRepeatsAsScenarios(true)...), which also carries the existing consolidate and parameterize options. See recording options
  • Response templates: new helpers matching WireMock's pickRandom, array, arrayJoin, range, formatXml, hostname and transformerParameters, and DataFaker locales. The lists helper has pickRandom, array, arrayJoin and range (Velocity $lists.pickRandom('a', 'b'), JavaScript lists.range(1, 5), Mustache {{#lists.pickRandom}}a|b{{/lists.pickRandom}} and {{#lists.range.1.5}}{{.}}{{/lists.range.1.5}}); pickRandom follows templateFakerSeed, and a list or range has at most 10,000 values. xpath.formatXml(xml) (Mustache {{#xpath.formatXml}}{{{ request.body }}}{{/xpath.formatXml}}) pretty-prints XML with the same DOCTYPE-refusing parser as the rest of MockServer, and fails on XML nested over 256 deep or output over four times its input (and over 64 KiB). hostname is the server's host name: the HOSTNAME environment variable when set (such as the pod name), otherwise looked up once in the background, never on the request path; it shows callers the server's internal name. faker.locale('de-DE') (Mustache {{ faker.locale.de-DE.name.fullName }}) gives DataFaker data for a locale; only the locales DataFaker ships are used (an unsupported country falls back to the language, an unsupported language to English with a warning), so a locale from request data such as Accept-Language cannot grow memory. arrayJoin output is capped at 64 KiB or four times its values. A template can now carry parameters, a JSON object it reads as parameters.* ($parameters.greeting, {{ parameters.greeting }}, parameters.greeting), in response, forward, forward-response and webhook templates; it is in the JSON schema and the OpenAPI spec, and every client sets it: Java template(...).withParameter("greeting", "hello") / withParameters(map), .NET HttpTemplate.OfType(...).WithParameter(...) / WithParameters(...), Go TemplateBuilder.Parameter(...) / Parameters(...) (HttpTemplate.Parameters), Node typings parameters, Python HttpTemplate(parameters=...) / with_parameter(...), Ruby HttpTemplate.new(parameters: ...) / with_parameter(...), PHP HttpTemplate::withParameter(...) / withParameters(...), and Rust Expectation::respond_template_with_parameters(...) / forward_template_with_parameters(...) and HttpOverrideForwardedRequest::response_template_with_parameters(...), read back with response_template_parameters() (the Rust HttpTemplate struct is unchanged, so code that builds it as a struct literal still compiles; the parameters are kept in the owning struct's extra map, so a retrieved expectation keeps them when it is upserted again); each leaves the field out when unset. The dashboard composer has a Parameters field on response and forward templates, keeps a loaded template's parameters, and every code tab writes them. Templates that use none of these, and responses that are not templated, run as before: Velocity and Mustache hold the new helpers in their shared bindings, and JavaScript binds them only for a template that names them
  • Spring tests: @MockServerInstance starts several named MockServers for one test class, like WireMock's @ConfigureWireMock. Repeat it once per service; each instance gets its own free port and sets mockserver.<name>.port and mockserver.<name>.baseUrl in the Spring Environment, plus any property names you list in portProperties or baseUrlProperties (these override application.properties and @TestPropertySource). A MockServerClient or ClientAndServer field annotated @InjectMockServer("<name>") receives that instance's client. initializationJsonPath loads expectations into one instance from a file, classpath resource, glob or file-system directory, and properties takes per-instance mockserver.* settings. Each instance is reset after every test, then restores the expectations it had at startup, and stops when the last Spring context using it closes (usually when the JVM exits, or earlier with @DirtiesContext). Test classes that share an instance must not run in parallel in one JVM. @MockServerTest is unchanged and can be used alongside it. See several named MockServer instances
  • Proxying: two new properties restrict where MockServer may send requests, like WireMock's proxy target allow and deny lists. forwardProxyAllowedHosts lists the only hosts that forward actions, proxied requests, replays and webhooks may go to, and forwardProxyDeniedHosts lists hosts they must not go to; each is a comma-separated list of host names (api.example.com), domains (*.example.com, which also matches example.com), IP addresses and CIDR blocks (10.0.0.0/8). The deny list wins over the allow list. IP addresses and CIDR blocks are also matched against the address a name resolves to (an IPv4 address written as IPv6 is matched as IPv4, and a listed loopback address also covers ::1, 0.0.0.0 and ::), and where MockServer connects to the target itself it connects to the address it checked, so DNS rebinding cannot get round them; through an upstream proxy, which resolves the name itself, it can. A refused target is answered exactly as a forwardProxyBlockPrivateNetworks refusal is (502 for HTTP, a closed connection for binary and WebSocket proxying, an unsent webhook, and a warning). Both are fixed at startup: PUT /mockserver/configuration ignores them with a warning, so a control-plane request cannot widen them. An invalid entry is rejected when set, and one that arrives another way (an environment variable or properties file) makes MockServer refuse every target until it is fixed. Both are empty by default, which leaves forwarding exactly as before. They also apply to fetching an OpenAPI spec from a URL, its remote $refs and redirects; specs read from a file or the classpath are unaffected. See forwardProxyAllowedHosts
  • Request matching by client IP address, like WireMock's withClientIp: "clientIp": "10.0.0.0/8" matches requests whose client address is that IP address or falls in that CIDR range, IPv4 or IPv6, and "!10.0.0.0/8" matches every other address (Java withClientIp, .NET WithClientIp / ClientIp, Go ClientIP, Python with_client_ip / client_ip, Ruby with_client_ip / client_ip:, Rust client_ip (held in extra, so no public field changed), PHP clientIp, and the Node typings' clientIp). The client address is the connection's TCP peer, or the source a PROXY protocol header names when transparent proxying is on; it is never read from X-Forwarded-For. With transparent proxying on, any client can send a PROXY header, so clientIp is a matching criterion, not access control. The range is parsed once when the expectation is created and an invalid value is rejected with the reason. Until the first clientIp is applied in the JVM (the switch covers every MockServer instance in the process and is never cleared, even by reset), requests record their address as before, with nothing extra allocated per request; each connection's request mapper is 8 bytes larger (72 rather than 64). After that, each connection keeps one 24-byte holder that also retains the peer's InetAddress (about 48 bytes for IPv4), and the address bytes (24 or 32 bytes) are derived on the first comparison. Matching adds no parsing, DNS lookup or allocation per request, and expectations without a clientIp run unchanged. It also filters verification and retrieved requests. The dashboard composer has a Client IP field, and its code generators emit each client's setter. Because the match diagnostics count every match field, totalFieldCount and matchedFieldCount in debugMismatch, explain and the 404 closest-match diagnostic are one higher (19 rather than 18); the ranking of expectations is unchanged. An expectation rejected by a model validation now reports the reason instead of only "exception while parsing".
  • Expectation metadata, and retrieving and clearing expectations by metadata: an expectation can carry a metadata object of any labels you want (string keys, any JSON values, such as an owner, a ticket number or tags), set when it is created and returned when it is retrieved. It never affects matching, and it is left out of the per-request match log. PUT /mockserver/retrieve?type=active_expectations and PUT /mockserver/clear accept metadataKey (optionally with metadataValue) and metadataJsonPath to select expectations by their metadata; a text value is compared as text and any other value as JSON. In the Java client, use withMetadata(...) when creating an expectation, retrieveActiveExpectationsByMetadata(...), clearByMetadata(...), clearByMetadataKey(...), and the new ExpectationFilter. The Node, Python, Ruby, Go, .NET, Rust and PHP clients support metadata, retrieving and clearing by metadata or unmatched, and paging too, under their own naming (for example clear_by_metadata in Python and Ruby, ClearByMetadata in Go and .NET); each refuses a blank key, a null value or a clear that selects nothing before sending anything. The dashboard shows an expectation's metadata, keeps it when you edit the expectation, and includes it in the generated code. See creating expectations
  • Unused expectations: unmatched=true on PUT /mockserver/retrieve?type=active_expectations returns only the expectations that have never matched a request since they were created or last changed, and on PUT /mockserver/clear removes them. Each MockServer instance keeps its own count. Java client: retrieveUnmatchedExpectations() and clearUnmatchedExpectations(). Like a namespace clear, a metadata or unmatched clear removes expectations only and leaves the event log as it is, and every filter you give (namespace, request matcher or id, metadata, unmatched) must match. An empty filter value (such as metadataKey=), unmatched=false on its own, a filter given twice, or a name that starts with metadata or unmatched but is not a filter (such as metadata_key) is rejected with 400 instead of clearing everything. Filter names are not case-sensitive; other unknown parameters are ignored, as elsewhere. Filter values are used literally, so a leading ! or ? is not a not or optional marker. A clear or retrieve whose query string cannot be decoded (for example a bare %) is now rejected with 400 rather than read as having no parameters, and so is a clear with an empty namespace=. See clearing and resetting
  • Paging: offset and limit on PUT /mockserver/retrieve for active_expectations (highest priority first, then oldest first) and recorded_expectations (in the order received), applied after every other filter and for every format. Java client: retrieveActiveExpectations(request, ExpectationFilter) and retrieveRecordedExpectations(request, offset, limit). An invalid value, or one of these parameters on a type it does not apply to, is rejected with 400. See retrieving active expectations
  • Static files: the new staticFilesDirectory property serves a directory of files for GET and HEAD requests that match no expectation and are not proxied, like WireMock's __files directory. A request for /assets/site.css gets assets/site.css from that directory, with a Content-Type from the file extension; a path ending in / gets that folder's index.html, and a HEAD request gets the file's Content-Length without the body. Expectations always win, and anything not served (a missing file, a folder, another method, a /mockserver/ control-plane path, or a file over 50 MiB) still gets the usual 404. Files are read on a small pool of their own, never on a network thread; with more than 1,000 reads waiting, further requests get a 503 (when MockServer runs as a WAR, files are read on the servlet container's request thread and this limit does not apply). Only plain paths inside the directory are served: .., encoded or double-encoded separators, backslashes, drive letters, control characters, hidden files and folders (names starting with .) and symbolic links out of the directory are refused, and folders are never listed. It is empty by default, which leaves unmatched requests exactly as before, and it is fixed at startup: PUT /mockserver/configuration ignores it with a warning. See staticFilesDirectory
  • Request matching with logical OR, like WireMock's or: an httpRequest holding an anyOf list matches a request when any one of its request matchers does, tried in order and stopping at the first match; "not": true beside it matches when none does. Each entry can be the usual request fields, an OpenAPI matcher, an if/then/else conditional or another anyOf, and a conditional's branches can now be an anyOf. It works for expectations, verification and retrieving recorded requests, and an empty anyOf is rejected. Every client builds it: Java requestAnyOf(...), .NET HttpRequest.RequestAnyOf / WithAnyOf, Go AnyOfRequest / RequestBuilder.AnyOf, Python AnyOfRequestDefinition, Ruby AnyOfRequestDefinition, Rust HttpRequest::any_of (held in extra, so no public field changed), PHP HttpRequest::anyOf, and the Node typings' AnyOfRequestDefinition. When a request matches none of the entries, explain, explainUnmatched and debugMismatch give the expectation's differences for the closest entry and add an anyOf object naming it (closestBranch, counted from 0) with each entry's differing fields; the Python, Ruby, Go, .NET and PHP diagnostics models read it, and the dashboard's mismatch dialog shows it. The dashboard composer keeps an anyOf (and an if/then/else) request matcher exactly as loaded when an expectation is edited, rather than adding form fields beside it that the server would reject; its code tabs write the branches in every language, and its match preview evaluates each one. Expectations without anyOf are matched and indexed exactly as before; an anyOf expectation is checked for every request (like a conditional one) and allocates nothing beyond its entries. See any of request matcher
  • Extensions, like WireMock's ExtensionFactory: your own Java classes implementing org.mockserver.extension.Extension that MockServer loads when it starts. Name them in the new extensions property (--extensions on the command line, MOCKSERVER_EXTENSIONS, -Dmockserver.extensions, @MockServerTest / per-instance @MockServerInstance properties) or pass classes or suppliers to Configuration.extensions(...) when embedded; extensionScanning=true also loads those listed in META-INF/services files. Each extension starts before MockServer builds anything else, is told the bound ports once they are bound (/mockserver/ready waits for it), and is stopped in reverse order when MockServer stops; an extension that also implements ExpectationInitializer seeds expectations at startup. A class that cannot be loaded, a duplicate or invalid name, or a start or onStarted that throws or exceeds extensionStartTimeoutMillis (default 30 seconds) refuses start-up and stops the extensions already started. The three properties are fixed at startup: PUT /mockserver/configuration ignores them with a warning, so the control plane can never load code. With no extension configured, nothing is scanned or loaded and request handling is unchanged. Works in the Netty server, both WARs (where onStarted gets no ports) and embedded use. See extensions
  • Extension admin routes, like WireMock's AdminApiExtension: an extension that also implements org.mockserver.extension.AdminApiExtension answers every request under /mockserver/extensions/<name>/. The routes are part of the control plane: the same authentication, browser-origin check, CORS headers and audit log apply, and with controlPlaneAuthorizationEnabled every route needs the role that may change state (GET included) unless the extension's isReadOnly says it only reads. The extension's code never runs on a network thread or MockServer's shared scheduler: at most extensionAdminMaxInFlight calls (default 32) run at once on a pool of their own, and a call beyond that, including one an extension makes back into MockServer, gets 503 (counted as extension_admin in mock_server_overload_rejections); a call that has not answered within extensionAdminTimeoutMillis (default 30 seconds) gets one 500 and is interrupted, and its late result is discarded. A call made while MockServer is stopping gets 503 and never reaches the extension. A thrown exception gets 500 with a correlation id. In a WAR the call runs on the request thread, with the limit but no timeout. Only loaded extension names are taken, so any other path under /mockserver/extensions/ behaves as before; an expectation loaded at start-up for a path under a loaded name logs a warning, because it no longer matches. Both properties are fixed at startup. With no admin extension loaded, nothing is built and request handling is unchanged. See serving your own control-plane routes
  • Extension listeners, like WireMock's ServeEventListener, StubLifecycleListener and GlobalSettingsListener: an extension that implements ServeEventListener is told about each request MockServer answered (mocked, forwarded or not matched, with the request, the response, the expectation id and when; bodies cut to maxLoggedBodyBytes); one that implements ExpectationLifecycleListener is told about each expectation added, updated or removed and why (API, FILE_INITIALISER, CLASS_INITIALISER, FILE_WATCHER, EXHAUSTED, EXPIRED, EVICTED or CLUSTER; an expectation used up across a cluster is EXHAUSTED and marked fleet-wide); and one that implements SettingsListener is told about each PUT /mockserver/configuration. Each listener extension runs on its own thread, so a slow or stuck listener never slows MockServer: events wait in a queue bounded by the new extensionListenerQueueSize (default 10,000) and, for served requests and expectation changes, by extensionListenerMaxBytes (default about 5% of the heap, shared between those listeners, and weighed by the bodies and expectations the events hold), and anything that does not fit is dropped and counted in mock_server_extension_listener_dropped_total without affecting the event log or verification. Both properties are fixed at startup. When MockServer stops, queued events are delivered for up to stopDrainMillis before the extensions stop. With no listener loaded, nothing changes: each removal or change of an expectation costs one check and allocates nothing. See listening to what MockServer does
  • Extension template helpers, like WireMock's TemplateHelperProviderExtension: an extension that implements org.mockserver.extension.TemplateHelperProvider adds its own helpers to response and forward templates under ext.<extension name> — $ext.acme.Strings.upper($request.path) in Velocity, ext.acme.Strings.upper(request.path) in JavaScript (the methods of a public interface you choose), and {{#ext.acme.upper}}{{request.path}}{{/ext.acme.upper}} in Mustache, whose helper receives the section after it is rendered. Each call runs on a helper thread of its own, never on a network thread; all the calls one template makes share one budget, the new extensionHelperTimeoutMillis (default 1 second), after which a running call is interrupted and the template fails as any failing template does; at most extensionHelperMaxInFlight (default 32) calls run at once, and one over the limit fails at once. Only httpResponseTemplate and httpForwardTemplate can call helpers: a webhook, secondary action, delay, file body, stream, load scenario or dry-run template that calls one, or a mistyped helper name, fails with an error instead of rendering as written. Helpers work over HTTP/1.1, HTTP/2 and HTTP/3 alike. Both properties are fixed at startup. Without such an extension, templates are unchanged and do no extra work. See template helpers
  • Extension request filters (experimental), like WireMock's request filters: an extension that implements org.mockserver.extension.RequestFilter sees every request that reaches expectation matching, over HTTP/1.1, HTTP/2 and HTTP/3 (plain and gRPC), in the WAR deployments and in InProcessMockServer (handle and handleAsync), and can let it through unchanged (FilterResult.proceed()), change it (proceedWith(request)), or answer it (respond(response)). It never sees control-plane requests, a CORS preflight for a control-plane path, requests refused by data-plane authentication, requests answered over HTTP/1.1 by a respondBeforeBody expectation, gRPC bidirectional streams answered by a grpcBidiResponse expectation, or the built-in gRPC health, reflection and chaos-profile answers, so a filter is not an access control: use dataPlaneAuthenticationRequired for that. It does see a browser's CORS preflight for a mocked endpoint (FilterContext.isCorsPreflight()), an unserved /mockserver/... path, and, over HTTP/3, /mockserver/status and /mockserver/ready, which HTTP/3 answers by expectation matching. Filters run on their own threads (extensionDataPlaneMaxInFlight, default 32) with a time budget (extensionDataPlaneTimeoutMillis, default 1000 ms), never on the threads that serve connections; a gRPC call's grpc-timeout includes the time its filter takes. A filter that throws, times out, returns null, moves a request onto the control plane or changes a gRPC request's method gets a 500 naming the extension unless its failOpen() is true (after a timeout, only if every later filter also fails open, and the later filters do not run); the event log records the received request once (as filtered), an INFO entry when a filter changed it, and a WARN for each failure. Pipelined HTTP/1.1 responses stay in order. A filter may change where a proxied request goes; forwardProxyBlockPrivateNetworks and the forward host allow and deny lists apply to it only when they are turned on. A request whose filter is still running when MockServer stops is answered 503 at once over HTTP/3, when a WAR is undeployed and when an InProcessMockServer is closed; over HTTP/1.1 and HTTP/2 the graceful stop drain (stopDrainMillis) lets it finish first, and it gets the 503 only if it outlasts the drain. Either way the filter threads end. The website's Extensions page shows how to write and load one, and examples/java has a runnable filter. With no such extension loaded nothing changes
  • InProcessMockServer now loads extensions configured with extensions (or configuration().extensions(...)); before, it ignored them. They start when the instance is created and stop in reverse order on close() or a refused start; onStarted gets an empty list of ports. Admin routes are reached by passing /mockserver/extensions/<name>/... requests to handle, with the same control-plane authentication as other REST requests, and run on the calling thread with no extensionAdminTimeoutMillis. Served-request and expectation listeners and $ext template helpers work as on a running server; a settings listener is never called, because PUT /mockserver/configuration is not available in-process. With no extensions configured nothing changes.
  • Extensions in Docker and Helm: every MockServer image now loads any JAR in /extensions, last on its classpath, so docker run -v $(pwd)/ext:/extensions:ro -e MOCKSERVER_EXTENSIONS=com.example.AuditExtension mockserver/mockserver adds extensions without building an image, in every variant including -graaljs, -clustered and -aot (use it instead of /libs, which those images fill themselves). Start-up is unchanged when nothing is mounted, and the images' start-up archives still apply. The Helm chart's new extensions.volume value mounts any volume (a ConfigMap, Secret, PersistentVolumeClaim, ...) read-only at /extensions, and extensions.classes and extensions.scanning set the two properties. MockServer now refuses to start when an enabled output file (persisted expectations, recorded expectations or recorded requests, the audit log or the memory-usage CSV files) would be written into the folder an extension was loaded from. See adding extensions
  • Extension response transformers (experimental), like WireMock's ResponseTransformerV2: an extension that implements org.mockserver.extension.ResponseTransformer sees each response MockServer sends over HTTP/1.1, HTTP/2 or HTTP/3, from the WAR deployments and from InProcessMockServer (mocked, forwarded and proxied responses, and the responses MockServer makes itself, such as its 404, overload 503, breakpoint aborts, rate-limit 429, chaos errors, failed templates and failed forwards, which it can tell apart with TransformContext.isGenerated()) and can return a changed one. It is given a copy, so a change it makes in place reaches nothing else. When it changes the body, a Content-Length it left unchanged is recalculated, but one it sets itself is sent as it is, so a wrong one breaks the response and its keep-alive connection; a forwarded body is as the upstream sent it, so a transformer that replaces a compressed body must also set or remove Content-Encoding. CORS and defaultResponseHeaders are added after it, so it cannot re

[!IMPORTANT]
✂ PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Enabled.

♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate
renovate Bot requested review from dhoard and fstab as code owners October 27, 2026 07:11
@renovate renovate Bot added the dependencies Pull requests that update a dependency file label Oct 27, 2026
@renovate renovate Bot added the dependencies Pull requests that update a dependency file label Oct 27, 2026
@renovate
renovate Bot enabled auto-merge (squash) October 27, 2026 07:11
@renovate
renovate Bot merged commit fa6d9c8 into main Oct 27, 2026
27 checks passed
@renovate
renovate Bot deleted the renovate/org.mock-server-mockserver-netty-no-dependencies-9.x branch October 27, 2026 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant