Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
d11523c
Initial commit
3dgiordano Jun 12, 2026
f1e4f6d
Checkstyle update and fixes
3dgiordano Jun 12, 2026
4b52698
Fixes
3dgiordano Jun 12, 2026
be9e07d
Fixes
3dgiordano Jun 13, 2026
10dbf92
Fixes
3dgiordano Jun 13, 2026
a4c3597
Fixes
3dgiordano Jun 13, 2026
9f0a719
Fixes
3dgiordano Jun 13, 2026
0ccf5ac
Fixes
3dgiordano Jun 13, 2026
519708d
Fixes
3dgiordano Jun 13, 2026
a5d2d82
Resolve merge conflicts and stabilize SSL keystore handling
3dgiordano Jul 2, 2026
4bf39fc
Merge branch 'development' into HTTP-PARITY-REGRESSION
3dgiordano Jul 10, 2026
e188710
Clean code
3dgiordano Jul 10, 2026
b3dce41
Fix User Agent logic
3dgiordano Jul 10, 2026
c0ffb63
Merge branch 'development' into HTTP-PARITY-REGRESSION
3dgiordano Jul 10, 2026
abd9233
Merge branch 'development' into HTTP-PARITY-REGRESSION
3dgiordano Jul 10, 2026
9cb1453
Merge branch 'development' into HTTP-PARITY-REGRESSION
3dgiordano Jul 15, 2026
863cb07
Migration CLI
3dgiordano Jul 16, 2026
915d118
Fix initialization
3dgiordano Jul 16, 2026
59757b8
Merge branch 'development' into HTTP-PARITY-REGRESSION
3dgiordano Jul 16, 2026
14e4344
Merge branch 'development' into HTTP-PARITY-REGRESSION
3dgiordano Jul 16, 2026
9bc1c07
Merge branch 'development' into HTTP-PARITY-REGRESSION
3dgiordano Jul 16, 2026
0d4d4db
Merge remote-tracking branch 'origin/development' into HTTP-PARITY-RE…
3dgiordano Jul 17, 2026
4ddf931
Merge remote-tracking branch 'origin/development' into HTTP-PARITY-RE…
3dgiordano Jul 17, 2026
00e2153
Recover clearContentDecoders and comments
3dgiordano Jul 17, 2026
6ab2197
Merge remote-tracking branch 'origin/development' into HTTP-PARITY-RE…
3dgiordano Jul 17, 2026
e280085
Cross compatibility 5.5 and 5.6.3
3dgiordano Jul 18, 2026
14943c5
Default minimum to JMeter to 5.5
3dgiordano Jul 18, 2026
0842832
Compatibility with JMeter 5,5
3dgiordano Jul 18, 2026
6a8a607
Matrix JMeter 5.5 and 5.6.3 for compatibility tests
3dgiordano Jul 18, 2026
47d884b
Merge remote-tracking branch 'origin/development' into HTTP-PARITY-RE…
3dgiordano Jul 18, 2026
3102024
Merge remote-tracking branch 'origin/development' into HTTP-PARITY-RE…
3dgiordano Jul 18, 2026
11bd1da
Fix, cross platform path format
3dgiordano Jul 18, 2026
7ab27ff
Revert, changes are not necessary
3dgiordano Jul 18, 2026
9d21985
Revert, changes are not necessary
3dgiordano Jul 18, 2026
c361ed8
Revert, changes are not necessary
3dgiordano Jul 18, 2026
4957a55
Merge remote-tracking branch 'origin/development' into HTTP-PARITY-RE…
3dgiordano Jul 18, 2026
ea67f05
Renaming
3dgiordano Jul 18, 2026
f5e62b8
Fix flaky tests in parity tests
3dgiordano Jul 18, 2026
3e0722c
Merge remote-tracking branch 'origin/development' into HTTP-PARITY-RE…
3dgiordano Jul 18, 2026
308401c
RFC 9110 Redirection and Legacy JMeter Redirect
3dgiordano Jul 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
* text=auto eol=lf
*.java text eol=lf
*.xml text eol=lf
*.properties text eol=lf
*.md text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
*.sh text eol=lf
12 changes: 11 additions & 1 deletion .github/workflows/ci-jmeter-compatibility.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,23 @@ jobs:
uses: actions/cache@v5
with:
path: ${{ github.workspace }}/.jmeter
key: jmeter-${{ runner.os }}-${{ hashFiles('pom.xml', 'src/test/resources/jmeter/**') }}
key: jmeter-${{ runner.os }}-${{ hashFiles('pom.xml', 'src/test/resources/jmeter/**', 'src/test/resources/jmeter-regression/**') }}
restore-keys: |
jmeter-${{ runner.os }}-

- name: Build project and jmeter-test bundle
run: xvfb-run -a mvn -U --batch-mode clean install

- name: Run JMeter HTTP parity regression (all tiers)
run: >-
xvfb-run -a mvn -Pjmeter-regression
-Dcheckstyle.skip=true
-DskipTests
-Djmeter.regression.tier=all
-Djmeter.regression.tolerateExternalServiceDrift=true
--batch-mode
verify

- name: Run compatibility matrix on Java 17
working-directory: target/jmeter-test
env:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ Temporary Items

### Maven ###
target/
cp.txt
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,15 @@ Uses **Maven 3** from the repository root:

Artifacts land under **`target/`**. Compilation uses the **`jmeter.version`** declared in **`pom.xml`**; at runtime install the packaged JAR against the JMeter build you intend to run and validate with a short smoke plan.

### HTTP parity regression (JMeter 5.6.3 test plans)

To compare **HttpClient4** with the migrated **BlazeMeter HTTP** sampler against Apache’s official `bin/testfiles` JMX plans, see **[docs/jmeter-regression.md](docs/jmeter-regression.md)**.

```bash
mvn -Dcheckstyle.skip=true package
mvn -Pjmeter-regression verify
```


<a id="readme-license"></a>
## License
Expand Down
11 changes: 5 additions & 6 deletions checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<module name="LineLength">
<property name="max" value="100"/>
<property name="ignorePattern"
value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>

<module name="TreeWalker">
<module name="SuppressionCommentFilter"/>
Expand Down Expand Up @@ -104,12 +109,6 @@
<module name="StaticVariableName"/>
<module name="TypeName"/>

<module name="LineLength">
<property name="max" value="100"/>
<property name="ignorePattern"
value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>

<module name="EmptyForInitializerPad"/>
<module name="EmptyForIteratorPad"/>
<module name="EmptyLineSeparator">
Expand Down
126 changes: 126 additions & 0 deletions docs/jmeter-regression.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# JMeter HTTP regression parity tests

This suite compares Apache JMeter **HttpClient4** (reference) with the migrated **BlazeMeter HTTP** sampler (`HTTP2Sampler`) using official JMeter `bin/testfiles` plans from release **5.6.3**.

## What it does

1. Runs the stock JMX with `-Jjmeter.httpsampler=HttpClient4` (no plugin).
2. Migrates HTTP Request samplers headlessly via `JmxBlazeMeterHttpMigrator`.
3. Runs the migrated plan with the plugin installed under `lib/ext/`.
4. Compares sample trees semantically (`HttpsampleResultComparator`): labels, success, response codes/messages, bodies, and normalized response headers. Timings and volatile headers are ignored.

## Regression groups

These groups are this project's own way of organizing which JMeter `bin/testfiles` plans run in which CI stage — not an Apache JMeter classification. All vendored plans (including the ones below) come from the same upstream source (see [Resources](#resources)).

| Group | Maven | Plans | Notes |
|-------|-------|-------|-------|
| **Core** (`tier=core`) | `-Pjmeter-regression` | `TEST_HTTP`, `ResponseDecompression`, `TestHeaderManager`, `TestCookieManager` | Default CI; mirror + managers |
| **Extended — HTTPS/Auth/HTML** (`tier=extended`) | `-Pjmeter-regression-extended` | `HTMLParserTestFile_2`, `TEST_HTTPS`, `Http4ImplDigestAuth`, `Http4ImplPreemptiveBasicAuth` | HTTPS + embedded HTML + auth |
| **External services** (`tier=external`) | `-Pjmeter-regression-external` | `TestKeepAlive`, `TestRedirectionPolicies` | Calls real third-party hosts; opt-in only, not in default CI |
| **JUnit parity** | `mvn test -Dtest=com.blazemeter.jmeter.http2.parity.*` | JUnit ports of Apache HTTP module tests | HttpClient4 vs plugin in-process; HTTP/1.1 — see [below](#junit-parity-apache-srcprotocolhttp-tests) |

`BUG_62847` / `Bug54685` are vendored under `5.6.3/` but exercise JMeter controllers/Java samplers only — not HTTP plugin parity.

`SlowCharsFeature` (optional): run with `-Djmeter.regression.tests=SlowCharsFeature -Djmeter.regression.enableSlowChars=true` once Jetty supports HttpClient4-style **CPS** throttling (`httpclient.socket.*.cps`). See [SlowCharsFeature](#slowcharsfeature) below.

### SlowCharsFeature

Apache batch plan `SlowCharsFeature.jmx`:

1. A setup thread sets `httpclient.socket.http.cps` and `httpclient.socket.https.cps` to **1500** (bytes per second).
2. The main sampler requests `https://jmeter.apache.org/...` with `Range: bytes=0-7000` (partial content).
3. Assertions expect **HTTP 206**, ~7001 bytes, and **elapsed time &gt; 5s** (download throttled to ~1.5 KB/s).

HttpClient4 implements CPS by wrapping socket streams with a rate limiter (`org.apache.http.impl.conn.SocketFactory` / connection socket config). **Jetty has no equivalent property**: you would need a custom `ClientConnector` / `EndPoint` wrapper that throttles `fill()`/`flush()` per connection, and decide how CPS applies across HTTP/2 multiplexing and HTTP/3 QUIC streams (HC4's model is per-TCP-socket). Until that exists, the plan stays behind `-Djmeter.regression.enableSlowChars=true`.

`TestKeepAlive` is compared on **http1-only** only (HttpClient4 never speaks HTTP/2; keep-alive / `Connection: close` are HTTP/1.1 semantics).

Use `-Djmeter.regression.tier=extended|external|all` instead of a profile, or override with `-Djmeter.regression.tests=PlanA,PlanB`.

## Prerequisites

- **Java 17+**
- **Maven 3.6+**
- Network access (first run downloads JMeter 5.6.3; Core/Extended plans may call external hosts).

## Build the plugin JAR

```bash
mvn -Dcheckstyle.skip=true package
```

## Run regression tests

```bash
mvn -Pjmeter-regression verify
```

### Options

| Property | Default | Description |
|----------|---------|-------------|
| `jmeter.regression` | `false` (`true` with `-Pjmeter-regression*`) | Must be `true` to execute regression ITs |
| `jmeter.regression.tests` | Core group list (see `pom.xml`) | Comma-separated JMX base names (no `.jmx`) |
| `jmeter.regression.tier` | _(unset)_ | `core`, `extended`, `external`, or `all` when `jmeter.regression.tests` is unset |
| `jmeter.regression.version` | `5.6.3` | JMeter distribution version to download |
| `jmeter.home` | _(auto)_ | Use an existing JMeter install instead of downloading |
| `it.http3` | `false` | Also run each plan with HTTP/3 profile |
| `jmeter.regression.protocol` | _(all profiles)_ | Filter: `http1-only`, `http2`, or `http3` (with `it.http3`) |
| `jmeter.regression.timeoutMinutes` | `10` | Per-run timeout |
| `jmeter.regression.tolerateExternalServiceDrift` | `false` (auto for Extended auth + External-services plans) | Skip strict compare when ref/plugin saw 5xx vs 2xx on the same sample (sequential runs vs flaky hosts) |

### Examples

Core group only, HTTP/1.1 profile:

```bash
mvn -Pjmeter-regression -Djmeter.regression.protocol=http1-only verify
```

Extended group — start with local HTML parser (no network):

```bash
mvn -Pjmeter-regression-extended -Djmeter.regression.tests=HTMLParserTestFile_2 verify
```

Full Extended group:

```bash
mvn -Pjmeter-regression-extended verify
```

External-services / optional batch plans:

```bash
mvn -Pjmeter-regression-external verify
```

### JUnit parity (Apache `src/protocol/http` tests)

Runs in the normal unit-test phase (`mvn test`), not Failsafe. Each test executes the same sampler configuration through **HttpClient4** and **HTTP2JettyClient** (HTTP/1.1 forced).

| Class | Apache source |
|-------|----------------|
| `HttpMirrorParityTest` | `TestHTTPSamplersAgainstHttpMirrorServer` (GET/PUT, POST urlencoded/multipart/raw, file upload) |
| `HttpMirrorItemisedParityTest` | same source — `itemised_testPostRequest_UrlEncoded` (0–7), `itemised_testGetRequest_Parameters` (0–5) |
| `HttpMirrorMultipartParityTest` | same source — `testPostRequest_FormMultipart` (0–6) |
| `HttpMirrorRawBodyParityTest` | same source — `testPostRequest_BodyFromParameterValues` (0–9) |
| `HttpMirrorFileUploadParityTest` | same source — `testPostRequest_FileUpload` (0–2) |
| `HttpRedirectsParityTest` | `TestRedirects` (301–308, no follow; incl. HEAD/PUT/DELETE) |
| `HttpRedirectsFollowParityTest` | redirect follow (`followRedirects` + `autoRedirects`) |
| `HttpCookieManagerParityTest` | `TestHC4CookieManager` (set/echo cookies) |
| `HttpCacheManagerParityTest` | `TestCacheManagerHC4` (embedded cache hit) |
| `HttpDisableArgumentsParityTest` | skippable args (5.6.3); `enabled` checkbox → JMeter 5.7+ |

```bash
mvn test -Dcheckstyle.skip=true -Dtest=com.blazemeter.jmeter.http2.parity.*
```

## CI

`ci-jmeter-compatibility.yaml` runs **all regression groups** (`tier=all`: Core + Extended + External-services) via `-Pjmeter-regression` after `mvn clean install` (which also runs JUnit parity via Surefire). External-service drift tolerance is enabled in CI for auth/keep-alive plans.

## Resources

Vendored under `src/test/resources/jmeter-regression/5.6.3/` from [apache/jmeter rel/v5.6.3 bin/testfiles](https://github.com/apache/jmeter/tree/rel/v5.6.3/bin/testfiles).
Loading
Loading