We have just released version 5.63.1. Here is a list of the changes that came with the release:
We have just released version 5.63.1. Here is a list of the changes that came with the release:
Highlights
Four things worth knowing about 5.63.1. 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
bodyFileNameon a mapping'sresponse, or on any entry of itsresponseslist, in place of an inlinetextvalue; 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: abodyFileNameon a response in one of the binary formats, a response that sets bothtextandbodyFileName, 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, which shipped in 5.62.1 with a release note and no page section: 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 has counted messaging references since 5.62.1, and file-message mappings have had file-backed response bodies since 5.62.1.
- 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 200and 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 500naming 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, answeredHTTP 500and removed nothing. It went on answeringHTTP 500for 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 isHTTP 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 ofHTTP 500and 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 POST /grpc/management/importMappings now reports an imported mapping that replaced an existing one separately from one that was added. An imported mapping is registered under a fresh id and written to the file its name maps to, and a mapping whose name ends in .json is written to the file of that name, so importing an instance's own export writes each such mapping over the file of the mapping it was exported from. The response listed only the new id among mappings, as though the mapping had been added, so a re-import could replace mappings without saying so. The response now also carries a replaced member, present only when at least one imported mapping took the file of an existing one, with one entry per such mapping giving its id and the supersededId of the mapping it replaced. The superseded mapping no longer backs a file on disk, and deleting the imported id does not restore it. An import that replaces nothing responds exactly as before. See Importing via REST API on the HTTP page, Importing via REST API on the gRPC page and the OpenAPI documentation.
- 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.

No comments:
Post a Comment