Thursday, 29 October 2026

Traffic Parrot 5.64.1 released, what's new?

We have just released version 5.64.1. Here is a list of the changes that came with the release:

Highlights

Five things worth knowing about this release. The full list of features, changes and fixes follows.

Traffic Parrot stands in for the CICS bridge

Set trafficparrot.ibmmq.cicsBridge.enabled to true and a native IBM® MQ virtual service answers CICS DPL bridge requests as the bridge does: the COMMAREA is decoded with the program's COBOL copybooks, the request is matched on the program name, the decoded fields and the MQCIH, and the reply's COMMAREA is encoded from JSON by the reply copybook. Nothing runs on the mainframe. See CICS over IBM® MQ.

Credentials stay out of a recording

Set trafficparrot.http.recording.redaction.enabled to true and the Authorization, Cookie, Set-Cookie and Proxy-Authorization headers, and any header you name, are masked in what recording saves to disk and in the per-request log entry, replaced by a placeholder or dropped. The client recording through Traffic Parrot still receives the backend's real headers. See Redacting credential headers during recording.

Your extension gets Traffic Parrot's database connections

An extension with a public constructor taking TrafficParrotServices is handed the JDBC connections named in database-connections.json, opened as the dataSource helper opens them, so a transformer that generates test data writes to the database the helper reads, with no copy of the credentials and no reflection into Traffic Parrot's classes. See Using Traffic Parrot's database connections.

Handlebars partials have a page of their own

Tools, Templates lists every .hbs file under the traffic files directory and edits it in place, offering Traffic Parrot's helpers by name as you type {{; the next response that includes the partial renders the saved version, with no restart. A partial can no longer include a file outside the traffic files directory. See Template files.

Testcontainers: your jars, your properties, and a Cypress suite

The Testcontainers module now supplies your extension jars and property overrides, withExtensionJar(Path) and withProperty(key, value), so an extension, a JDBC driver and a database-connections.json of your own load from an unedited release zip, and the example workspace shares one Traffic Parrot across every test class in the JVM. A Cypress suite does the same with the testcontainers npm package, starting Traffic Parrot once before the first spec and stopping it after the last. See Adding your extensions and configuration and From a Cypress suite.

Also in this release: SFTP and FTP connections pick up complete files only, a native IBM® MQ request matcher can read its value from a file, a webhook can read the headers of the response it follows, the Settings page shows the database connections and the extension jars, a property set on the command line is honoured when the properties file leaves it out, a signed-in user without the edit role can no longer change anything, and a mock OAuth2/OIDC provider is scaffolded into a scenario in one request.

Features

  • Traffic Parrot stands in for the IBM® MQ CICS bridge. With trafficparrot.ibmmq.cicsBridge.enabled set to true, a native IBM® MQ virtual service answers CICS DPL bridge requests (format MQCICS: an MQCIH, the program name and the COMMAREA) as the bridge does. The COMMAREA is decoded with the program's COBOL copybooks, named in copybooks/programs.json, and the request is matched as a JSON view of the program name, the decoded fields, the MQMD user and the MQCIH fields, with the new matchesCicsField matcher (a field path and equalTo, matches or a comparison) or any JSON matcher. The reply is built from the mapping's JSON: the settable MQCIH fields, and the COMMAREA encoded with the reply copybook or given as hex, with the cicsField helper echoing request fields into it; it goes to the request's reply-to queue, correlated to its message id. A text message on a queue whose mapping uses CICS fields is warned about in the log, and the MQCIH Authenticator never reaches the log. The IMS bridge is not covered. See CICS over IBM® MQ.
  • Credential headers can be redacted while recording. Set trafficparrot.http.recording.redaction.enabled to true in trafficparrot.properties and the Authorization, Cookie, Set-Cookie and Proxy-Authorization headers, plus any header named in trafficparrot.http.recording.redaction.headers, are masked in what recording saves to disk: the recorded response headers, and the request headers recording matches on. trafficparrot.http.recording.redaction.strategy chooses between PLACEHOLDER, which replaces each value with trafficparrot.http.recording.redaction.placeholder (***REDACTED*** unless you change it) and so changes what the saved request matcher expects, and DROP, which leaves the header out. The client recording through Traffic Parrot still receives the backend's real headers, a mapping created in the web user interface or through the admin API is saved as written, and the per-request entry in the log shows the covered headers redacted the same way, whether or not a recording is running. Off by default. See Redacting credential headers during recording.
  • A custom extension can use Traffic Parrot's database connections. An extension with a public constructor taking a TrafficParrotServices, from the trafficparrot-sdk, is created with it, and services.databaseConnections().dataSource(connectionId) is a javax.sql.DataSource for the JDBC connection of that id in database-connections.json, opened as the dataSource helper opens it. So a transformer that generates test data writes to the database the helper reads, with no copy of the credentials and no reflection into Traffic Parrot's classes, and an edit to the file takes effect on the next call. This works for every extension type, and an extension without such a constructor is created as before. The SDK workspace project gains InsertRecordWithDatabaseConnection, a transformer that inserts a record and answers with its id, with a unit test that passes it an in-memory database. See Using Traffic Parrot's database connections.
  • A webhook (callback) can read the headers of the response Traffic Parrot served, as {{originalResponse.headers.headerName}}, beside {{originalResponse.status}} and {{originalResponse.body}}, in every template a callback has: URL, method, headers, body and the enabled condition. A header the mapping's response template rendered is read as rendered. The shape is that of {{request.headers}}: the name matches in any case, and a header sent more than once keeps every value, {{originalResponse.headers.headerName.[1]}} being the second. See Request data.
  • Handlebars partials have a page of their own. Tools, Templates lists every .hbs file under the traffic files directory by its path, templates/sql/customer-exists.hbs for example, with a filter, and Edit opens it in an editor that shows the name a template includes it with, {{> templates/sql/customer-exists.hbs}}, and offers Traffic Parrot's helpers by name when you type {{, inserting the chosen helper's snippet. Save writes the file, and the next response that includes the partial renders the saved version, with no restart. A save over a file that changed on disk since it was opened is refused with the reason shown, and Edit reloads it. The page lists, loads and saves the files through the same mechanism as the Current configuration files editor on the Settings page, so trafficparrot.gui.config.files.allowedFilesToEdit governs which files it lists, and loading and saving need traffic-parrot-gui-edit-role when the web user interface login is on; a user without the role sees the list and a notice saying so. Creating or deleting a partial is still done on disk. See Template files.
  • SFTP and FTP connections pick up complete files only, and pick up a file of a name already answered. A file in a polled request directory is picked up once it has been listed with the same size and modified time at two consecutive polls, so a large file still being uploaded is left alone until it is complete. Names matching trafficparrot.file.remote.temporaryFileNames, *.tmp,*.part,*.filepart unless you change it, are never picked up, and nor are Traffic Parrot's own in-progress uploads. A file that matches a mapping is now deleted from the request directory before the response is written, on every driver, so a client that uploads its next file under the same name as soon as it sees the response has that file picked up too; previously a second file of a name already picked up in the session was never picked up. A file that matches no mapping stays and is not picked up again unless it changes. The two-poll check and the property apply to SFTP and FTP connections only. See How files on SFTP and FTP servers are picked up.
  • A native IBM® MQ mapping's request bodyMatcher can now read its expected value from a file under __files, by replacing the matcher's inline value with a bodyFileName object, as a JMS or file message mapping's request matcher can. The file is read when the mapping loads, resolved against the __files directory beside the mapping's ibm-mq-mappings directory (per scenario for scenario-scoped mappings) and contained to it. Only the top-level bodyMatcher supports it: a bodyFileName nested inside and, or or not, or one naming a file that does not exist, is refused when the IBM® MQ mappings load, with an error that names the mapping file, the matcher and the file. The Static Files page counts the file as in use and exporting a scenario bundles it. The IBM® MQ mapping schema served at /schemas now describes the file form on the top-level bodyMatcher only, and the JMS and IBM® MQ schemas now require at least two operands in an and or or, which is what the mapping loader has always required, so trafficparrot validate reports a one-operand form before the mapping is loaded.
  • The Testcontainers module supplies your extension jars and property overrides. withExtensionJar(Path) copies a jar into the container's lib/external directory before it starts, so an extension or a JDBC driver loads with no edited release zip, and withProperty(key, value) adds a key=value override to the start command of a container built from a release zip, as if the property were set in trafficparrot.properties. The example workspace gains two examples that share one Traffic Parrot across every test class in the JVM, one of them started with an extension, a driver and a database-connections.json of its own. See Adding your extensions and configuration and One Traffic Parrot for the whole suite.
  • The Settings page shows the database connections and the extension jars. Two read-only cards. Database connections lists the selected scenario's database-connections.json, each connection with its id, type, URL, driver, user and other settings and every password shown as ****; the file is read as the dataSource helper reads it, so a file the helper would refuse shows the helper's reason. Extension jars lists the .jar files in lib/external, the directory the launcher puts on the classpath, with each jar's size and modified time. Each card opens from a deep link, /settings.html#collapseDatabaseConnections and /settings.html#collapseExtensionJars.
  • A mock OAuth2/OIDC provider, scaffolded in one request. POST /api/http/oauth2-mocks writes three ordinary HTTP mappings into the selected scenario: an OpenID Connect discovery document whose endpoint URLs resolve against the address the request arrived on, a JWKS, and a token endpoint that issues RS256-signed JWTs for the client_credentials grant, so a system under test discovers the mock by its issuer, fetches its keys and obtains tokens as it would from a real provider, and a real OIDC client library completes both without special handling. The request body sets the mock's name, the issuer (which also decides where the endpoints are mounted), the audience, extra claims, the token lifetime (zero or negative issues an already-expired token, for testing expiry), the public base URL the mock advertises behind a proxy or on a published port, and an optional client id and secret the token endpoint then requires. Each mock signs with its own RSA key, kept in the token mapping, so the key id it publishes and every token it issued stay valid across a restart; a second scaffold of the same name, or onto paths other mappings already answer, is refused with 409 rather than replacing the key. When the web user interface login is on, the request needs traffic-parrot-gui-edit-role. See Mock OAuth2/OIDC provider.

Changes

  • trafficparrot.http.recording.request.storage.rewrite.regex rules now also apply to multipart/form-data request bodies, part by part. Previously a multipart body was recorded unchanged, with no warning, even when rules were set, so a credential in a form field was saved raw. Each part is now decoded with the charset from its own Content-Type (UTF-8 when it names none) and the rules run on that part on its own, so a rule cannot match across a part boundary. A part that a rule matches is re-encoded in full, as a non-multipart body already was, so a rule that matches inside a file upload re-encodes that part: keep uploads out of a recording by giving the Recording URL a path rather than by relying on the rules to miss them. A rewrite that leaves a JSON part invalid is treated as one that leaves a JSON body invalid: the exchange is not recorded, and an error naming the URL and the rules is written to the log. An installation that sets no rules is unaffected. See Rewriting request bodies during recording.
  • While trafficparrot.http.recording.request.storage.rewrite.regex rules are set, the Request received: entry the log writes for each request to the virtual service now shows the request body as the rules rewrite it, whether or not a recording is running, and closes with a line naming what was redacted. Previously the entry carried the body as received, so a rule that kept a credential out of the recorded mapping did not keep it out of the log. trafficparrot.virtualservice.httpRequestLoggingDisabled=true still removes the entry, an entry the rules leave unchanged is as before, the URL is logged as received, and the request journal, which the HTTP Request Log page shows, keeps the body as received. See Rewriting request bodies during recording.
  • When an MCP tool fails unexpectedly, the answer is now a tool result with isError: true whose text names the tool and the reason, for example Tool 'list_scenarios' failed: Permission denied. The cause is in the Traffic Parrot log., where before it was a JSON-RPC -32603 error with a fixed message that pointed only at the log. MCP 2025-11-25 files a tool's failure under tool execution errors, which the client shows to the assistant, whereas a protocol error it may drop; so the assistant can act on the reason. The reason is a permission error, a missing file, a file that already exists, the operating system's text for any other file-system failure, or otherwise the type of the exception, and never a path, because the response goes into the assistant's context and on to its model provider. The cause, with its stack trace, is still in the Traffic Parrot log. A call in a batch that fails in dispatch, before its tool runs, is still -32603. See Running a tool.
  • An MCP tool argument that the tool rejects, as missing, of the wrong type or out of range, is now answered as a tool result with isError: true whose text names the argument, for example Argument 'id' is required or Argument 'dryRun' must be true or false, but was 'no', where before it was a JSON-RPC -32602 error. MCP 2025-11-25, the revision Traffic Parrot advertises, classifies input validation as a tool execution error so that the client shows the message to the assistant, which can then correct the call, whereas a protocol error the client may drop. A call no tool gets to judge is still -32602: tools/call without a name, a tool this instance does not offer, or arguments that is not an object. The AGENTS.md brief in the install directory says the same. See the error-code table.
  • A matchesXPath request body matcher given an expression alone, with no value sub-matcher, now matches only when the expression selects at least one node or evaluates to boolean true. An expression that evaluates to a number, a string or false used to match every XML body, so count(//item) = 99 matched a body with two items; it now matches none, and count(//item) = 2 matches the two-item body alone. This applies to HTTP, JMS and native IBM® MQ mappings alike, and the matcher's description in the editor's request matchers help panel now says the same. A mapping that relied on such an expression matching should select a node, or compare the value, instead. See the matchers reference.
  • Upgraded bundled JRE from 21.0.11 to 21.0.12.1
  • The shipped trafficparrot.gui.config.files.allowedFilesToEdit gains .hbs$, so the Current configuration files editor on the Settings page now lists Handlebars partials among the files; the Templates page under Tools is the place to edit them (see Template files). An installation upgraded with the upgrade script keeps the list its trafficparrot.properties already holds, as it keeps every setting, so add .hbs$ to it yourself to see partials on either page; a fresh installation has the new list.
  • Saving a file in the Current configuration files editor on the Settings page, or a partial on the Templates page, now keeps an LF-only file's line breaks: a file that uses LF alone is saved with LF. A file with CRLF or mixed line breaks, a file with no line break, and a new file are saved with CRLF, as every saved file was before. A JSON file does not notice the difference; a plain-text partial served as a response body does.
  • The gRPC mapping editor shows request metadata as rows by default, in a Request metadata matching card below the request and response panels, with a matcher per entry (equal to, contains, a regular expression or a date/time comparison), and exception metadata as rows on the Exception tab. The key: value text form of each is under Edit as text beneath its rows, as for HTTP request and response headers, and the two stay in step. What is saved is unchanged: a mapping saved without an edit is the same mapping. See Request metadata.
  • The wss4j, opensaml, openws and xmltooling libraries are no longer bundled. Traffic Parrot never used them: SOAP message signing uses Apache Santuario (xmlsec) directly, which is still bundled. A plugin, or a JAR under lib/external, that relied on finding them on the classpath must now bring its own copy.

Fixes

  • The HTTP Request Log now shows the status the client received, in the Status column and in the row's details alike, including when a {{modifyResponse 'statusCode' 404}} response template or a proxied backend set it after the mapping was matched. Previously the column showed the status in the mapping's response definition, so a row read 200 for a response the client received as 404 while the details panel showed the served one. A mapping with no status now reads 200, the status it is served with, where the cell was blank, and a fault that sends no status line leaves the cell blank, where it read 200. See Assert a request was made.
  • With trafficparrot.gui.security.mode set to LOGIN_PROPERTIES, the log pages that every virtual service HTTP and HTTPS port serves under /__logs (/__logs/virtualservice/, the Traffic Parrot log, and /__logs/access/, the access logs; a scenario's own ports included) now ask for the web user interface login. They answered anyone who could reach the port, with no login, while the same pages on the web user interface port asked for one. Stubs and the admin API on the virtual service ports are unchanged, and so is the Logs page, which reads them through the web user interface port with the login you signed in with. Read the pages through the Logs page or the HTTPS port rather than a plain-HTTP port, which sends the password unencrypted.
  • An Azure AMQP 1.0 connection in jms-connections.json loads and connects again, where one such entry used to stop every JMS connection in the file loading after an upgrade. An entry whose jmsProvider is not one Traffic Parrot knows is now skipped, with one WARN in the log naming the entry and the file it is in, rather than stopping the other connections loading. See JMS connections.
  • JMS mappings recorded through an IBM® MQ 10 client now replay. That client adds a JMS_IBM_MsgToken property to every message it receives and refuses to set it again, so a mapping that had recorded it could not be replayed: each matching request was logged as skipped and no response was sent. New installations no longer record the property, which joins the default of trafficparrot.jms.skipRecordingProperties. A mapping recorded earlier still carries it, and Traffic Parrot now sends the response without it and writes a WARN naming the property to the log, where before no response was sent. The same applies to any other JMSX or JMS_ property the JMS provider refuses to set; a refused property outside those two namespaces still fails the response as before, because it is more likely a mistake in the mapping than a property the provider owns. Upgrading keeps the skip list your installation already has, so if you record through an IBM MQ 10 client, add JMS_IBM_MsgToken to it yourself.
  • Saving or deleting a mapping from a mappings list page now closes the edit dialog and refreshes the list when the list's automatic reload is stopped, or is paused because a tooltip is open or text on the page is selected. The reload that a save or delete issues could be cut short by the next paused reload tick, so the dialog stayed open and the list went on showing the mapping as it was before the change until the automatic reload ran again. This applies to the HTTP, gRPC, JMS, native IBM® MQ, file message and WebSocket mappings lists alike.
  • Opening the HTTP or gRPC mappings list no longer changes what a mapping with a response body file serves, and neither does an OpenAPI export, a mapping schema check at startup, or a save of a gRPC mapping whose file name Traffic Parrot generates, which read the mappings the same way. With trafficparrot.virtualservice.mapping.cache.milliseconds above 0 (the shipped performance-profile-settings/trafficparrot.performance.properties sets 10000000), listing the mappings made such a mapping answer with the text the list shows in place of its file until the cache expired: (Traffic Parrot: not a text body) for a binary file, the Problem loading body file message for a file that could not be read, or a text file's contents as they were when listed.
  • A mapping whose response bodyFileName points outside the scenario's __files directory no longer breaks the HTTP and gRPC mappings lists. The list request failed, so the page showed no mappings, and adding a gRPC mapping failed too, until that mapping was deleted. The mapping is now listed with the reason its file cannot be read. trafficparrot validate now treats such a mapping as it treats one whose body file is missing, validating the reason as the response body, where before it stopped with exit code 2 on an HTTP mapping and skipped a gRPC one. The OpenAPI export, which failed on such a mapping, now completes with the reason as that mapping's response body, and the mapping schema checks at startup, which stopped Traffic Parrot starting, now report the mapping only where an OpenAPI specification or proto covers it.
  • The HTTP and gRPC editors now refuse a response body file name outside the scenario's __files when the save keeps a binary body, including a re-save of an existing mapping that already names one (such a mapping could not serve its body anyway).
  • The gRPC management file endpoints, /grpc/management/file/<name> on the management port, no longer put a filesystem path in an error response. A name outside the scenario's __files directory, or naming that directory itself, is refused with 400 and Invalid file name on every verb, where before a save or delete answered 500 with the directory's absolute path, a delete named at the directory removed it when it was empty, and a read of it answered its listing. A missing file answers 404 and File not found, and any other failure a fixed 500 message, with the detail in the log. The gRPC export leaves out a body file whose name is refused, where before the zip was cut short at that mapping.
  • A JMS, native IBM® MQ or file message mapping whose response body file is missing from __files, or resolves outside it, is now reported without filesystem paths. The mappings list, which the Add/Edit page loads, a delete or edit of any mapping of that protocol, and the AsyncAPI and Pact specification imports answered 500 naming the body file and the mapping file by their paths; the reason now reads, for example, Body file 'reply.txt' referenced from mapping 'orders.json' was not found., with the paths in the log. The export, which answered a 200 zip with its __files entries missing, now answers that 500 too. A zip import that cannot write an entry reports Could not import zip entry '<entry>' rather than the entry's path.
  • An IBM® MQ, JMS or file message mapping file with no id now shows correctly on the Add/Edit page and can be edited and deleted there. Previously the list raised a DataTables warning, the edit dialog opened empty, and while such a file was present, editing or deleting other mappings could fail.
  • Loading and saving files under Settings, Current configuration files, now needs traffic-parrot-gui-edit-role when the web user interface login is on (trafficparrot.gui.security.mode=LOGIN_PROPERTIES). A signed-in user holding traffic-parrot-gui-role alone was offered Load, and the /api/configuration/files endpoints answered that user any listed file's content and accepted its changes and new files, so the shipped readonly user could read, change or create any .json, .csv or .properties file the editor lists. Those endpoints now answer 403 naming the role, and the page shows such a user the file names and a line saying which role loading and saving need; the names list stays open to every signed-in user. File names on the page are now shown as written, where a name containing markup was rendered as HTML. With the default ALLOW_ALL nothing changes. See Role based access and trafficparrot.gui.config.files.notAllowedFilesToEdit.
  • ActiveMQ, RabbitMQ, FTP and SFTP connection passwords, and the internal broker's keystore password, no longer appear in trafficparrot.log. The lines that report a JMS connection being created and a messaging record or replay page being rendered printed each configured connection with its password; they now print [REDACTED] in its place, and a password that is not set is still shown as not set, so a failed login can be told from a missing password. Native IBM® MQ connection passwords were already masked. Log files written before the upgrade (logs/trafficparrot.log*) still hold them.
  • An OpenAPI response keyed by a range such as 4XX no longer fails the import. The specification allows 1XX to 5XX as response keys, and importing one stopped with For input string: "4XX" after the operations before it had already saved, from the Import page, the skeletons upload and the generate_mappings_from_openapi MCP tool alike. A range response is now imported at the first code in the range, 400 for 4XX, and where the specification also defines that code explicitly the explicit example is the one imported. OpenAPI request validation (trafficparrot.openapi.request.validation.mode) answers an invalid request whose validation response is keyed by a range at that first code too, where before such a request was served unvalidated and an error was written to the log. See Matching response code.
  • The Admin request received: entries in the log, one per request to the virtual service's admin API, no longer carry the value of an Authorization or Proxy-Authorization header. The value is replaced with ***** and the scheme is kept, so an entry reads Authorization: [Basic *****]. With trafficparrot.gui.security.mode set to LOGIN_PROPERTIES the web user interface sends the signed-in user's own credential with every admin API request it makes through /api/http, and Traffic Parrot sends an internal one on every start, so both were written to the log as sent. The Request received: entry for stub traffic is redacted while credential header redaction is on or request storage rewrite rules are set, and otherwise carries the request's headers as received.
  • An equalToXml request body matcher with namespaceAwareness set to NONE now matches a document whose elements have siblings, such as a SOAP envelope holding a Header and a Body, and an element holding both text and child elements (mixed content) now matches under every namespaceAwareness setting. Neither matched before. See JSON-only matchers.
  • A matchesJsonSchema request body matcher whose schema is written as a JSON object rather than as a string, through the admin API or in a mapping file, is now saved and read back as an object, and a schemaVersion the mapping does not declare is no longer added to it. Saving through the admin API used to rewrite the matcher, turning the object into an escaped string and appending "schemaVersion": "V202012". What the matcher matches is unchanged: a mapping that declares no version still validates against JSON Schema 2020-12. The Add/Edit mapping editors add no schemaVersion either, and a mapping whose schema is an object keeps that form through an unedited Save. See JSON-only matchers.
  • Saving a mapping in the HTTP, gRPC, JMS, native IBM® MQ or file message Add/Edit mapping editor now keeps the request body matcher options the editor cannot author, whether or not the matcher value was edited: a matchesJsonSchema keeps its schemaVersion, an equalTo its caseInsensitive, an equalToJson its ignoreArrayOrder and ignoreExtraElements, and a matchesJsonPath its advanced form with a value sub-matcher. Save rebuilt the matcher from the dropdown and the text field alone, so a schema declaring V4 was saved with V202012, which can change what it matches, and the other options were dropped. An option is kept while the matcher chosen in the dropdown stays the same, and dropped when you choose another matcher. A request body matcher that reads its value from a file (bodyFileName) still loses its sibling options on Save. Also fixed in the HTTP and gRPC editors: after opening a mapping whose request body matcher reads from a file, saving a different mapping with an inline request body no longer writes that body into the first mapping's file. See JSON-only matchers.
  • A Handlebars partial can no longer include a file outside the traffic files directory. A partial's name is resolved against the selected scenario's directory, or the traffic files directory when no scenario is selected, and a name that resolves outside it, or to a file that does not exist, is now reported as a missing partial, with the reason in the log. A mapping that included a file from outside the traffic files directory now answers an error: move the file under it. See Template files.
  • Upgrading with the upgrade script now keeps the web user interface user store and the Handlebars partials, and prints no setting values. trafficparrot.gui.login.properties was not carried, so an installation with the login on came up with the shipped admin and readonly users and their password; it is now copied whole, so a shipped user you removed stays removed. The templates and responses directories and any .hbs file at the installation root are carried too, where before a partial had to be copied by hand. The upgrade's output, which it also writes to tp.out in the new installation, now names each carried setting without its value, where before it printed the value of every setting it merged: passwords, the MCP token and a licence passed as -Dtrafficparrot.license.content among them. If you upgraded an installation before this change, check that its trafficparrot.gui.login.properties holds only the users you want, and that its tp.out holds nothing you would not keep in a log. The standalone upgrade helper script, which takes the release zip and the previous installation as its arguments, carries the same files, merges a directory on macOS, where before it carried nothing, copies a directory reached through a symbolic link as that directory's contents, and ends with an error naming what it could not copy rather than reporting a complete upgrade. See Upgrading Traffic Parrot.
  • Under the web user interface login (trafficparrot.gui.security.mode=LOGIN_PROPERTIES), a signed-in user without traffic-parrot-gui-edit-role can no longer change Traffic Parrot. The role was checked only when a page was drawn, so the mapping editors hid their controls from such a user, but nothing checked it on the way in: the shipped readonly user could create a mapping through POST /api/http/__admin/mappings, duplicate one, start a recording, switch the scenario, clear a request log, upload a static file or run an import, from the pages that still showed those controls and through every API the web user interface port serves. Every request that could change Traffic Parrot now needs the edit role: any method other than GET, HEAD and OPTIONS, and any request carrying a body. A user with traffic-parrot-gui-role alone is refused with 403 and a JSON body naming the role, which the pages show as the error. The request log queries that take their criteria in a POST body stay open to a reader: requests/find, requests/count, the near-miss queries and mappings/find-by-metadata through /api/http, the gRPC and JMS request counts, and the WebSocket messages/find and messages/count. On /mcp, a tool that changes something is refused as a tool result with isError: true naming the role, before it runs; listing the tools and calling the seven that only read is unchanged. Stub traffic forwarded to the virtual service through the web user interface port now needs the edit role too while the login is on; the virtual service port itself, its /__admin API and the management ports are not behind the login, as before. With the default ALLOW_ALL nothing changes. See Role based access.
  • A -D system property, including one set through JAVA_TOOL_OPTIONS, or a key=value argument to the start script now sets a property that the loaded properties file leaves out. Such an override was ignored and the bundled default applied, which the startup log listed under Using default values from as if nothing had been set: on an installation upgraded from an earlier release, whose trafficparrot.properties lacks every property added since, or with a file named by trafficparrot.properties.filename that holds only the keys you set, -Dtrafficparrot.virtualservice.http.port=2001 left the virtual service on 8081, and -Dtrafficparrot.http.recording.redaction.enabled=true left redaction off. The startup log's Using system property overrides line now lists such a key. A fresh installation's shipped file holds every property, so it was unaffected. Precedence is unchanged: a system property wins over the file, which wins over the bundled default.
  • The dataSource helper now works on a fresh installation with database-connections.json as shipped. The shipped file carries a Couchbase example, and reading any connection from a file that holds one needed the Couchbase client, which the release does not bundle, so every dataSource call failed with NoClassDefFoundError: com/couchbase/client/core/env/Authenticator, the JDBC connections beside the example included, until the example was deleted from the file. The file now reads without the client. A Couchbase connection used while its JARs are not in lib/external answers an error naming lib/external, and the JDBC connections beside it work. See Data source.
  • An unmatched native IBM® MQ or file message is now listed on that protocol's Requests page with its near misses, the mappings closest to it and the field each one missed on, when trafficparrot.messaging.diagnostics.enabled is true, and Clear empties the list, as on the JMS page. The property named all three protocols but only JMS was connected, and JMS recorded nothing at the shipped mapping cache setting, trafficparrot.virtualservice.mapping.cache.milliseconds=0, so its page stayed at 0 MESSAGES however the property was set. The messaging request journal is now safe to write from several receive threads at once: a JMS virtual service with more than one request destination, or whose Requests page was read during traffic, could leave a received message unanswered, and the journal could grow past trafficparrot.virtualservice.maxRequestJournalEntries until restart. With the property false, near misses are no longer computed. Near misses are listed under the selected scenario, whichever scenario's virtual service received the message, and a file whose name does not match is shown as a Body Content mismatch.
  • A JMS response property value containing ; now keeps it when the mapping is saved, including when the mapping is opened and saved without changes: everything after the second ; on a name;type;value line is the value. A line without a type or a value is refused with a message naming the line, instead of an error that pointed to the logs. See Response properties.
  • The size helper now counts the entries of a map, as the WireMock helpers list says it does: {{size request.query}} and {{size (parseJson request.body)}} render the count, where before each rendered an error such as {{ size ERROR: cannot get size of 'java.util.HashMap' }} in the response. A string or a list is counted as before. A map that stands for a failed helper is not counted: when the map comes from a dataSource call that reported an error, size renders that error rather than 0, where before it rendered an error naming an internal class.
  • Internalize under Tools now writes each response column of a native IBM® MQ mapping with several responses into the response it was exported from. The MappingData.csv header names each response's queue, but every column was written into every response, so after an Internalize, even of an unedited export, all the responses of such a mapping carried the body of whichever column was processed last. A column whose header names a queue the mapping no longer sends to is refused with an error naming that queue and the queues the mapping does send to, and the rest of the file is applied as before. A mapping with one response is unaffected.
  • A native IBM® MQ response that could not be scheduled within a minute, because the request queue's Maximum messages in progress, the largest value among the mappings that read the queue, were all in flight, was dropped: the request had already been consumed, the log read Skipping IBM MQ message, and no response was sent. Traffic Parrot now keeps waiting for a slot and writes a WARN, Still waiting to schedule a response message to <queue>, to the log, and the requests it has not yet consumed stay on the queue. A queue is unaffected while any mapping that reads it leaves the field at its default.
  • Under concurrent requests, with trafficparrot.virtualservice.mapping.cache.milliseconds at its default of 0, a request could intermittently be answered 900, no responses matched, although a mapping matched it, most often where request matcher scripts sat above a fallback mapping. Every request now matches against the complete set of mappings. The same fix covers gRPC, and JMS and IBM® MQ when a non-zero mapping cache is set.
  • With trafficparrot.virtualservice.handlebars.throwErrors=true, a dataSource query that fails, because the connection id is unknown, the table is missing, the SQL is invalid or the database is down, is now answered with an HTTP 500 even when the helper is given a default, and so is every other error the helper would otherwise write into the body: no rows with no default, a placeholder with no value, and a single value asked of a query that returned several. A query that runs and returns no rows with a default still serves the default. With the property at its default of false, a failure the default covers is now logged at WARN, Returning default value '[]' because of error '...', where before it was logged at INFO. A custom helper that reports an error without throwing can honour the property through the new protected static throwErrorsEnabled() on TrafficParrotHelper.
  • An HTTP extension named in trafficparrot.http.responsetransformers that cannot be loaded, such as a jar carrying its own copy of the SDK or a class that is not on the classpath, no longer stops the HTTP virtual service from starting, and the HTTP pages of the console no longer fail on it: it is left out, with an ERROR in the log naming the class and the reason, and the other extensions are loaded. A mapping file that cannot be loaded, because it is not valid JSON, is not a mapping, or names a request body file that is missing, is left out the same way, with one ERROR naming the file and the reason, and the other mappings, HTTP and gRPC, are still served, where before every mock answered 500 while the file was broken, or Traffic Parrot did not start. A validate run still fails on such a file.
  • The HTTP and gRPC mapping lists show each mapping file left out that way, in a warning above the table: one line per file, or per mapping of a file whose mappings fail one at a time, naming it by its path exactly as the ERROR in the log does, with the reason, so the line can be searched for in the log. The warning is refreshed with the list's automatic reload and goes once the file loads; before, the log was the only sign. GET /http/management/skippedMappingFiles and GET /grpc/management/skippedMappingFiles on the management port answer the same list as JSON.
  • A dataSource query on a database connection that contains a colon which is not a placeholder, such as the time literal '12:30' or the cast 42::varchar, now runs. The helper's own check for placeholders without a value read every :name in the SQL as a placeholder, so it refused such a query before it ran, with Parameter values not specified: [:30, :varchar]. For a database connection the check now follows the same named-parameter rules the query runs with: a colon inside a quoted literal, a quoted identifier, a comment or a :: cast is not a placeholder, and a placeholder the call gives no value is still refused by name. Because the check now reads the SQL as the database does, its refusals shift with it: a placeholder inside a comment is no longer reported, and &name, :id.json and :age2 given only age are refused as missing before the query runs, where before the query failed inside the driver and default covered it. CSV and Excel sources are unchanged, and still treat a quoted :name as a placeholder. See Data source.
  • The CA truststore shipped as certificates/virtualservice-CA-certificate.jks now holds the current Traffic Parrot CA certificate, the same one as certificates/virtualservice-CA-certificate.pem and the one every shipped server certificate is issued from. It still held the CA from before the December 2025 rotation, so a client that trusted the keystore trusted none of the shipped certificates and failed the TLS handshake; a client trusting the PEM was unaffected. The alias, trafficparrot-ca, and the password are unchanged, so a client configured for the keystore needs only the new file.
  • The SDK workspace project zip now builds on a machine whose Maven cache does not already hold org.unsynchronized:jdeserialize, a library the SDK depends on that Maven Central does not host: the zip's repo/ directory carries it next to the SDK, with its licence under licenses/. The workspace's build stopped at Could not find artifact org.unsynchronized:jdeserialize:jar:2.0.2 unless an earlier build had already cached it. The zip also no longer carries a .surefire-<hash> file, the test-run statistics left behind by the build that produced it.

Documentation

  • Added CICS over IBM® MQ: how Traffic Parrot stands in for the CICS DPL bridge, the copybooks and their index, the request view, matching, replying, and a walkthrough that runs end to end on the free IBM® MQ developer container with a sample program and a Java client; and Traffic Parrot for CA LISA / DevTest users, which maps DevTest's IBM MQ transport and copybook data protocol onto those pieces and says what is not covered. The IBM® MQ matchers table gains matchesCicsField, and the properties reference the trafficparrot.ibmmq.cicsBridge.enabled row.
  • The MCP endpoint reference now lists get_body_file: a read-only tool that returns one of the selected scenario's __files entries as text, by the name get_mapping reports as bodyFileName, so an assistant can see a response body that is stored in a file instead of inline. Only that scenario's __files directory is readable, a binary file is refused with its size, and contents past 64 KB come back cut short with truncated set. The tool counts on that page and on Generate mocks with an AI agent now read thirteen tools, seven of which only read.
  • The HTTP and gRPC pages and the OpenAPI documentation now describe the POST /http/management/importMappings and POST /grpc/management/importMappings responses as the endpoints answer them: the ids of the imported mappings, plus warnings when part of an HTTP upload could not be imported. The HTTP page also says that an imported mapping records the file it came from as tp.import.source and that importing a file of the same name again replaces the earlier import, and both pages say what happens when an imported mapping is written to the file of an existing one.
  • Added a Hoverfly middleware in Traffic Parrot how-to page for users migrating from Hoverfly. It maps Hoverfly's Modify/Synthesize middleware, a language-agnostic executable fed the HTTP pair as JSON on stdin and returning mutated JSON on stdout, onto Traffic Parrot's external-process response transformer, with a concept-mapping table (Hoverfly's global -middleware flag against the global opt-in plus per-mapping transformers/transformerParameters model), a copy-pasteable uppercase-body Python example, the versioned envelope migration notes, and the differences to expect (response-only, one process per request, HTTP only, per-mapping opt-in).
  • Added Traffic Parrot in JUnit and Spring Boot tests to the user guide: how the build starts and stops Traffic Parrot around the integration-test phase with the Maven or Gradle plugin, how a test points the code under test at its port, how to reserve every port at build time when builds share a CI host, and what to clear between tests (the request journal, scenario state and key-value state). The For WireMock Users page's reset row now names those three calls; it named the mapping cleanup feature, which consolidates recorded mappings into a new scenario and is not a reset.
  • Added When each extension runs: where each extension type sits relative to request matching and response rendering. A Handlebars helper runs during rendering; an HTTP response definition transformer and the JMS, Native IBM MQ and file message response transformers run before it, on the mapping's response as written; an HTTP response transformer runs after it, on the response the client is about to receive.
  • Added Adding an extension jar and configuration without rebuilding the zip: the lib/external directory, and the three ways to set a property from outside the zip, on the command line, through JAVA_TOOL_OPTIONS, or in a properties file of your own that holds only the keys you set, each with its Docker form and the startup log line that confirms it.
  • The templates on the Per-test data recipe page, and in its example zip, are now laid out across lines with indentation: a helper call's arguments one per line inside the tag, where they render nothing, and a JSON response one field per line. The request matcher script that decides the 409 keeps rendering exactly true or false, with a ~ on each tag beside the layout and a comment saying why. The responses they render are unchanged.
  • Added From a Cypress suite to the Testcontainers page: a cypress.config.js that builds the image from the module's Dockerfile with the testcontainers npm package, starts Traffic Parrot once in setupNodeEvents with your jars and traffic files copied in and your properties on the start command, hands its URL to every spec through the returned config, and stops it on after:run, with the two specs that use it.

Tuesday, 22 September 2026

Traffic Parrot 5.63.2 released, what's new?

We have just released version 5.63.2. Here is a list of the changes that came with the release:

Highlights

Four things worth knowing about this release. The full list of features, changes and fixes follows.

Start Traffic Parrot from your JUnit tests

The new Testcontainers module starts Traffic Parrot in a container from a release zip, or from an image you already have, mounts your mappings and hands the test the URL to point at. TrafficParrotClient stubs, verifies and resets over the admin API from the same test, and a downloadable example workspace runs a JUnit 5 and a JUnit 4 example against a release zip with nothing else configured.

@Container
static final TrafficParrotContainer TRAFFIC_PARROT = TrafficParrotContainer
        .fromReleaseZip(Paths.get("/downloads/trafficparrot-no-jre-5.63.1.zip"))
        .withPackagedTrafficFiles(MountableFile.forClasspathResource("traffic-files"));

Signed webhooks, in both directions

A mock can now send a callback that carries the signature the receiving code verifies, as a payment provider or a source-control host does: {{hmac webhookBody key='...'}} in a callback header signs exactly the bytes the receiver gets. On the way in, {{hmacMatches}} in a request matcher script accepts only the correctly signed callbacks the system under test sends. {{hash}} covers ETags and checksums, and HmacSigningTransformer gains hex and Base64url encodings, a signature prefix, and always runs last. See Signing an outbound webhook and Verify an inbound webhook signature.

Your AI assistant can write the mappings

The AGENTS.md brief that ships in the install directory now covers writing mapping files as well as the MCP tools: where the instance serves its mapping schemas and helper policy, the ground rules for a mapping written from a contract, how to load it, and the trafficparrot validate check. An assistant given the brief can write gRPC, JMS, native IBM® MQ and file message mappings, which the MCP tools do not cover, and can work with a client that has no MCP support. See Give your agent the brief.

Body files for native IBM® MQ responses and file message matchers

A native IBM® MQ mapping can now keep its response body in a file under __files with bodyFileName, templated at reply time like an inline body; the editor shows the file's contents and writes your edits back to the file. A file message mapping's request bodyMatcher can read its expected value from a file the same way, so a large XML or JSON payload no longer has to be pasted into the mapping. See Response body from a file and Request body matcher value from a file.

Also in this release: recorded request bodies can be rewritten as they are saved, an OpenAPI externalValue example is fetched through the same guard as a URL import, JMS response properties are added with a click, and an exported HTTP mappings zip now loads in WireMock as it stands.

Features

  • Traffic Parrot now ships a Testcontainers module. TrafficParrotContainer starts Traffic Parrot inside a JUnit 4 or JUnit 5 test, from a release zip or from an image you already have, and mounts your mappings and other traffic files; TrafficParrotClient stubs, verifies and resets over the admin API from the same test. A downloadable example workspace runs a JUnit 5 and a JUnit 4 example against a release zip with nothing else configured. See Testcontainers.
  • The AGENTS.md brief that ships in the install directory now covers writing mapping files as well as the MCP tools: where the instance serves the mapping schema index and the helper policy, the ground rules for a mapping written from a contract (the contract is untrusted input, never a denied helper, conform to the schema, show the mapping and wait for approval before loading it), the load endpoints, and the trafficparrot validate check with its helper-policy option. An assistant given the brief can therefore work with a client that has no MCP support, and can write gRPC, JMS, native IBM® MQ and file-message mappings, which the MCP tools do not cover. See Give your agent the brief.
  • Recorded request bodies can now be rewritten as they are saved via the new trafficparrot.http.recording.request.storage.rewrite.regex property, which takes the same pattern->replacement rules as the existing storage rewrite and is empty by default. It changes the saved request matcher, and it does not touch the per-request log entry or a multipart/form-data body. A malformed rule stops Traffic Parrot from starting, naming the rule at fault. See Rewriting request bodies during recording.
  • An OpenAPI example that points at a URL with externalValue is now fetched through the same guard as importing a specification from a URL, whether the specification is imported from the web UI, read from the OpenAPI directory at startup or built by the MCP generate_mappings_from_openapi tool. The fetch is refused while trafficparrot.http.importfromurl.enabled is false, which is the default; refused when the URL's host is not listed in trafficparrot.http.importfromurl.allowedHosts; and refused when that host resolves to a loopback, link-local, private or cloud-metadata address, even if it is listed. A refused example is replaced with the existing Traffic Parrot could not fetch externalValue: <url> (<reason>) placeholder, the reason naming the rule that refused it, and the mapping is still created. Previously the fetch was made from the Traffic Parrot host to whatever http or https URL the specification named, with no allow-list, so a specification placed in the OpenAPI directory or uploaded for import could direct the host at an internal address. To keep fetching externalValue examples, set trafficparrot.http.importfromurl.enabled=true and list the example hosts in trafficparrot.http.importfromurl.allowedHosts; see Properties.
  • New Handlebars helpers {{hmac}} and {{hash}} compute an HMAC signature or a keyless digest (SHA-256, SHA-1 or MD5) of any value, written as lower case hex, Base64 or Base64url. Webhook headers and bodies are templates, so a mock can now send a callback that carries the signature the receiving code verifies, as a real payment provider, source-control host or chat platform does, something the HmacSigningTransformer, which signs responses only, could not do. See Signatures and digests and the worked webhook-signing example.
  • A signature header can now name the body it signs. A webhook's URL, method and header templates read the rendered callback body as {{webhookBody}}, and a response's header templates read the rendered response body as {{renderedBody}}, so {{hmac webhookBody key='...'}} in a callback header signs exactly the bytes the receiver will verify, instead of restating the body expression and keeping the two copies in step by hand. Both keys are absent when nothing was rendered: a webhook with no body, or a response that is binary, has no body, is a proxy response, or loads a body file with templating disabled. A mapping that also lists another body-changing transformer should sign with hmacSigning, which runs last, rather than with {{hmac renderedBody}}. See Signing a body from a header.
  • New {{hmacMatches}} helper verifies the signature on an inbound request from a request matcher script, so a mock can accept only the correctly signed callbacks the system under test sends and answer the rest with an error. It compares in constant time and renders false, never an error, for a missing or malformed signature. See Verify an inbound webhook signature.
  • The HmacSigningTransformer gains an encoding parameter (base64, which is the default and the previous behaviour, hex or base64url) and a signaturePrefix parameter (for example sha256=), and it is now guaranteed to run last, after every other response transformer on the mapping including a ResponseTransformerV2 an install adds through trafficparrot.http.responsetransformers, so the signature always covers the bytes the client receives; a response that also passes through external-process, for example, is signed over the transformed body. The transformers that ship with Traffic Parrot run before the signature is computed, so a mapping that combines hmacSigning only with those is unaffected. A mapping that also lists a body-changing ResponseTransformerV2 of your own after HmacSigningTransformer in trafficparrot.http.responsetransformers now signs the transformed body; the signature previously covered the body before that transformer ran, so it did not match what the client received. See HmacSigningTransformer.
  • A native IBM® MQ response body can now be served from a file in the __files directory, as a JMS or file message response body can be. Set bodyFileName on a mapping's response, or on any entry of its responses list, in place of an inline text value; the two are mutually exclusive, and the path is resolved against the __files directory beside the mapping's ibm-mq-mappings directory, per scenario for scenario-scoped mappings. The file is read as UTF-8 text and is supported for the MQFMT_STRING message format only: a bodyFileName on a response in one of the binary formats, a response that sets both text and bodyFileName, or a file that does not exist, is rejected at load time and the IBM® MQ mappings fail to load until it is corrected, with an error naming the mapping and the file. The file's contents go through the same Handlebars templating as an inline body. The mappings list shows the file name in place of the body, the editor shows a read-only Response from file: note and loads the file's contents into the response body field, and saving writes the edit back to the file and keeps the reference. The Static Files page counts the file as in use, and exporting a scenario bundles it. Support for binary (bytes) response bodies read from a file, and for reading an IBM® MQ request body matcher's value from a file, will follow.
  • A file message mapping's request bodyMatcher can now read its expected value from a file under __files, by replacing the matcher's inline value with a bodyFileName object, as a JMS mapping's request matcher already could. The file is read when the mapping loads and is contained to __files. Only the top-level bodyMatcher supports it: a bodyFileName nested inside and, or or not, or on fileNameMatcher, is refused when the file message mapping loads, as a JMS mapping already refuses one, with an error that names the mapping file, the matcher and the file. The JMS and file message mapping schemas served at /schemas, which trafficparrot validate and an AI agent writing mappings work from, now describe exactly that: the file form on the top-level bodyMatcher only. See Request body matcher value from a file.
  • WebSocket service virtualization is now documented on the new WebSocket page and gains a Record page. With trafficparrot.websocket.enabled=true, clients connect to the virtual service HTTP port with Connection: Upgrade and Traffic Parrot answers each text frame from the mappings in websocket-mappings/, which follow the selected scenario; a mapping can also push a frame to a client as soon as it connects. The WebSocket menu carries Add/Edit, Record and Logs pages, and the /websocket/management API creates, lists, updates and deletes mappings and queries the frame journal. The new Record page proxies every WebSocket connection to a real backend and saves each client frame together with the backend's reply as a mapping; frames the backend sends before the client has spoken are saved as on-connect mappings, and recording the same frame again replaces the earlier recording rather than duplicating it.

Documentation

  • The HmacSigningTransformer section now says that the transformer is opt-in and must be listed in the mapping's response transformers array, and its example includes the array, which the earlier example omitted; a second example shows a hex signature with a prefix. The new Signatures and digests section documents the three helpers and the renderedBody and webhookBody model keys, Signing an outbound webhook walks through a complete signed callback, and Verify an inbound webhook signature shows the matcher-script recipe.
  • The MCP endpoint documentation now carries an error-code table: every JSON-RPC error code Traffic Parrot answers (-32700, -32600, -32601, -32602, -32603, -32000 and -32001), when each one is answered, and the HTTP status it arrives under, plus the refusals that deliberately carry no JSON-RPC envelope (the 405, 415 and 406 request-framing refusals, and the login's role-mismatch 403), so a client integrator can tell the layers apart. The Who can reach it section now also distinguishes all three authentication refusals rather than two of them.
  • The gRPC import REST endpoint, POST /grpc/management/importMappings, is now documented under Importing via REST API on the gRPC page and in the OpenAPI documentation, where the POST /http/management/importMappings success response is now described as the {"mappings": [...]} object the endpoint returns, with the warnings member a partial import adds, rather than as a bare array of ids. Download the specification.
  • The JMS page now documents reading a request body matcher's value from a file: the bodyFileName form, the matchers that accept it, the top-level-only rule and what a bad reference does at load. Two stale notes on the same page are corrected: the Static Files page counts a file a JMS mapping references as in use, and file message mappings can serve a response body from a file.
  • The new WebSocket page documents WebSocket service virtualization end to end: enabling it, the mapping file format, message and on-connect triggers, how a frame is matched, the Add/Edit, Record and Logs pages, the /websocket/management API and its message journal, recording from a real backend and its limitations. WebSocket support had shipped switched off behind trafficparrot.websocket.enabled with a properties entry and no page of its own.

Changes

  • A malformed rule in trafficparrot.http.recording.response.rewrite.regex or trafficparrot.http.recording.storage.rewrite.regex now stops Traffic Parrot from starting, naming the rule at fault. Previously such a rule was dropped with a line in the log and recording carried on without it, so a rule that had not taken effect was discovered only by finding the un-rewritten value in a recording. Check both properties before upgrading an installation that sets them.
  • The executeProcess helper is now supplied by the four helper lists in trafficparrot.properties (trafficparrot.http.handlebars.helpers, trafficparrot.jms.handlebars.helpers, trafficparrot.ibmmq.handlebars.helpers and trafficparrot.file.handlebars.helpers) rather than being registered on every list whatever the property says, so it can be switched off by removing com.trafficparrot.handlebars.script.ExecuteProcess from them, as evaluate already could be. The shipped lists name it, so a stock installation is unchanged. An installation that overrides any of those properties keeps its own list, which does not name the helper, so after the upgrade a template that uses {{executeProcess ...}} on that protocol no longer renders, and nothing at start-up says so: add com.trafficparrot.handlebars.script.ExecuteProcess to each overridden list to keep the helper. The performance profile, whose four lists are empty, no longer offers it, as it already did not offer evaluate.
  • Upgraded the Couchbase Java SDK that the Couchbase data source is built against from 3.12.2 to 3.12.3; the java-client and core-io JARs to place in lib/external are linked from that page.

Fixes

  • The Current configuration files editor on the Settings page no longer offers trafficparrot.gui.login.properties, the web user interface's own user store, or the database-connections.json, ibm-mq-connections.json and jms-connections.json files that hold connection passwords. The shipped trafficparrot.gui.config.files.notAllowedFilesToEdit list did not name them, so any user who could open the page could read them with Load, and a user allowed to edit could rewrite them with Save. The shipped list now names the user store and every file whose path contains -connections.json; an installation that overrides that property keeps its own list, so add both entries to it.
  • Saving a file in the Current configuration files editor on the Settings page no longer removes its blank lines. A run of consecutive line breaks was collapsed into one on the way to disk, so a blank line between two sections of a properties file, or an empty row in a CSV data file, was gone after Save. Each line break is now written back as one CRLF, blank lines included.
  • The Externalize page under Tools no longer breaks its own round trip when a field value contains a comma or a double quote. Such a value was written to MappingData.csv without quoting, so its row gained extra columns, and Internalize then failed with an error even on an unedited export. Values are now written and read as standard CSV, so an export edited in a spreadsheet and internalized keeps every value in its own column, and a hand-edited row with the wrong number of columns is refused with an error that names the row.
  • Importing a zip of mappings no longer fails outright because one entry inside it has a name that cannot be used as a file name. Such a name (one holding a NUL byte, which no platform accepts, or, on Windows, one holding : \ < > " | ? or *) previously ended the whole import with HTTP 500: the rest of the zip was not read, and for an HTTP or gRPC mapping zip that meant none of its mappings were imported at all. The offending entry is now skipped on its own and everything else in the zip imports as normal, with Ignoring zip entry <name> (its name is not a usable file name) written to the log so that nothing is dropped without a record. The answer to the import is the same as when nothing was skipped: for the HTTP import, HTTP 200 and the ids of the mappings that were imported, with no warning for the entry that was not. A script that checks only the status code or the answer body therefore does not see the skip; the log line is the only record. This applies to the HTTP, gRPC, JMS, native IBM® MQ and file message mapping imports alike, and to a .zip of proto files uploaded with Import skeleton. The name that a zip entry may carry is a property of the platform Traffic Parrot runs on, so this most often shows up when a zip written on Linux or macOS, where such a name is legal, is imported into Traffic Parrot running on Windows. An ordinary exported zip is unaffected and imports exactly as before.
  • Importing a zip of mappings now restores a response body file to the sub-directory it was exported from. A mapping whose response is read from a file below __files (a bodyFileName such as grpc/response.json) exports with the file at that path, but the import wrote the file at the top of __files under its bare name, so on an instance that did not already hold the file the imported mapping could not find it: an HTTP mapping answered HTTP 500 naming the missing file, and a gRPC mapping answered INTERNAL with <bodyFileName> not found, until the file was moved by hand. The file is now written where the mapping expects it. This applies to the HTTP and gRPC mapping imports; the JMS, native IBM® MQ and file message imports already kept the path. An exported zip whose body files all sit at the top of __files is unaffected and imports exactly as before.
  • Exporting the HTTP mappings now writes every mapping in the zip as a .json file, so an exported zip unpacked into a WireMock root directory loads every mapping it holds. A mapping keeps the name it was given, whether it arrived through the admin API or in a mapping file that carries its own name, as a stub recorded by WireMock does, and the export previously used that name as the file name as it stood, so a mapping named Get order 1001 became the entry mappings/Get order 1001, with no extension. A mapping file with no name of its own is named after the file, and a mapping saved from the Add/Edit HTTP mappings editor is always named <name>.json, so those already carried the extension. Traffic Parrot imported such a zip either way, but WireMock reads only files with the .json extension from its mappings directory, so it silently loaded none of the mappings exported without it. Every entry is now written as mappings/<mapping name>.json, whether the zip is downloaded with the Download Mappings button on the Export page or through the Management API. Nothing else in the zip changes: importing it back into Traffic Parrot is unchanged, and a mapping whose name already ends in .json is exported exactly as before.
  • The gRPC Add mapping form no longer carries an exception mapping's metadata and error details onto a successful mapping saved after it. The form keeps its values after a save, and switching from the Exception tab to the Successful tab left the exception metadata and error details in place behind it, so the next mapping saved from the Successful tab replied with status OK but with the earlier mapping's metadata and a grpc-status-details-bin header attached to the reply. Switching to the Successful tab now clears them, and a mapping saved with a successful status never carries error details, whichever tab it was saved from.
  • Clearing the HTTP stub mappings no longer fails when one of the mapping files holds more than one mapping. A file in the mappings directory may carry several mappings in a single JSON document ({"mappings": [ ... ]}) rather than one mapping per file, and Traffic Parrot loads and serves those mappings normally. Clearing them, however, answered HTTP 500 and removed nothing. It went on answering HTTP 500 for every attempt that followed until Traffic Parrot was restarted, including after the file itself had been deleted from disk. The mappings are now cleared, the answer is HTTP 200, and such a file is deleted whole, exactly as the mapping files holding a single mapping already were. A file holding several mappings that earlier clears left in place is deleted by the next clear, with every mapping it holds, so copy any such file you still need before clearing. This applies to all three endpoints that clear the mappings: POST /api/http/__admin/mappings/reset, DELETE /api/http/__admin/mappings and POST /api/http/__admin/reset. Saving or deleting one individual mapping that was loaded from a file holding several is unchanged and is still refused, because that one mapping cannot be rewritten out of the file it shares with the others; edit the file on disk instead. An installation whose mapping files each hold a single mapping is unaffected and clears exactly as before.
  • Editing a recorded gRPC mapping in the gRPC Add/Edit mapping form no longer discards the CallId and MessageNumber values that mapping carries. Traffic Parrot writes these as response headers in the mapping file when it records a streaming call, to group the messages belonging to one RPC call and to order them (see gRPC streaming modes). The form does not offer them for editing, and it previously saved the mapping back without them, so opening a recorded mapping and clicking Save dropped both values even when nothing had been changed. A recorded server streaming, client streaming or bidirectional streaming call then stopped replaying in order, or stopped replaying as one call, as soon as any single message of it had been edited in the GUI, and putting the values back meant editing the mapping file by hand or recording the call again. Every response header a mapping already holds is now carried through a save unchanged. A mapping that holds none, such as a unary mapping added by hand, is unaffected and saves exactly as before, and editing exception metadata is unchanged.
  • Sending the gRPC management API (POST /grpc/management/mapping and PUT /grpc/management/mapping/{id}) a body that cannot be read as a mapping now returns HTTP 400, carrying the reason in the same primingResult field these endpoints already use to report a refusal, instead of HTTP 500 and a stack trace in the log. For example, a request matcher written as {"containing": "b"}, where the key the mapping grammar defines is contains, is answered with {"containing":"b"} is not a valid match operation; a body that is not JSON at all is answered with the reason it could not be parsed. Nothing is created or updated in either case, as before. The gRPC Add/Edit mapping form is unaffected, because it cannot produce such a body.
  • The message shown when a mapping cannot be saved because another mapping already owns its file name now quotes the name you typed, and names the file the two mappings land on. In the Add/Edit HTTP mappings editor the refusal previously quoted that file name where the mapping name belonged, so a mapping renamed to orders:new was refused with Mapping 'orders_new.json' cannot be saved because its file name clashes with another mapping's., naming a string that appears nowhere on the form. It now reads Mapping 'orders:new' cannot be saved because its file name 'orders_new.json' clashes with another mapping's. and goes on, as before, to explain how a file name is derived from a mapping name, so you can see both the name to change and the file the two names collapse onto. The MCP create_mapping and update_mapping tools return the same message; there the name was already reported correctly, and the file name is the new part. Which saves are refused is unchanged, and so are the mapping name Traffic Parrot stores and the file it writes.
  • WebSocket mappings are now loaded from the selected scenario. Every scenario directory has always been created with a websocket-mappings folder beside its mappings, grpc-mappings, jms-mappings, ibm-mq-mappings and file-mappings folders, but the WebSocket simulator only ever read the folder at the traffic files root, so a mapping placed in a scenario's folder was silently ignored: it did not appear on the WebSocket Add/Edit page, nothing was written to the log, and a client that connected to its channel was accepted and then got no reply. Mappings are now loaded from websocket-mappings in the selected scenario, or from the traffic files root when no scenario is selected, as the other protocols' mappings are, and the WebSocket Add/Edit page and the /websocket/management API follow a scenario switch together with the virtual service. A mapping saved while a scenario is selected is written to that scenario's folder. If you have kept WebSocket mappings at the traffic files root and work with a scenario selected, move them into that scenario's websocket-mappings folder, because the root folder now serves only when no scenario is selected. A mapping left at the root while a scenario is selected is not served: it is not listed on the Add/Edit page, nothing is written to the log, and a client that connects to its channel is accepted and then gets no reply. The file is not moved or deleted. The traffic files root is the default scenario, the one that is active when Traffic Parrot starts, so an installation that has never activated another scenario is unaffected. A mapping file written by an earlier version of the WebSocket simulator, whose only top-level property is webSocket, is not in the current mapping format: it loads as a catch-all mapping that matches incoming frames and replies nothing, and Traffic Parrot now writes a warning naming the file to the log, once per file, with the instruction to delete it, because such a file carries no id and cannot be edited or deleted from the Add/Edit page. See WebSocket System Simulation Properties.
  • A mapping with a scenarioName that is added through the API while Traffic Parrot is running is now listed once by GET /api/http/__admin/scenarios. Previously it appeared twice in that scenario's mappings array, so a test or tool that counted a scenario's mappings saw every mapping added at runtime twice. Matching, the scenario's state, its possibleStates and its transitions were unaffected, and mappings loaded from the mappings directory at startup were always listed once.
  • Importing a zip of mappings through POST /http/management/importMappings or the HTTP Import page now records the zip's file name on each mapping it registers, as tp.import.source in the mapping's metadata, as the HAR, OpenAPI, RAML and Pact imports already did, and importing a zip of the same name again first removes the mappings the previous import registered, so a re-import replaces its earlier import; previously nothing linked a re-imported mapping to its earlier copy, and whether it replaced that copy or was added beside it depended on the file each was written to. A mapping registered any other way is not removed by an import, although an imported mapping whose name ends in .json is written to the file of that name, so importing an instance's own export writes over the file of the mapping it was exported from. The response is unchanged: the ids of the imported mappings. See Importing via REST API on the HTTP page.
  • A backslash now escapes a {{...}} expression that is the first one in an HTTP response body. Disabling {{...}} documents \{{...}} as the way to leave one expression as written, but when the escaped expression was the first in the body the backslash was kept in the output and the expression was substituted anyway; an escaped expression anywhere after the first was already left alone. The escape now works in every position, so a body that starts with an escaped expression renders it as written, without the backslash. A body whose first expression is not escaped renders exactly as before.