# REST Assured 7.0.0

> Release notes for REST Assured 7.0.0.

REST Assured 7.0.0 no longer includes a path engine or Groovy, and sends the requests with the JDK’s own HTTP client instead of Apache HttpClient 4. GPath, the path syntax REST Assured has always used, is a dependency that you add yourself, and so is Apache HttpClient 5 if you want it instead of the JDK client. 7.0.0 also adds a second path engine, JSONPath with Jayway, and clients, so that tests that run in parallel can each have their own settings.

7.0.0-RC1 is the release candidate for 7.0.0. Please try it, and report what doesn’t work in the [issue tracker](https://github.com/rest-assured/rest-assured/issues).

## What’s new in 7.0

* **Path engines.** `rest-assured` no longer includes a path engine, and so no longer brings Groovy. Add `json-path-gpath`, and `xml-path-gpath` for XML and HTML, and your paths keep working. They are the new names of `json-path` and `xml-path`. See [Upgrading to 7.0](/release-notes/7-0/#upgrading-to-70) and [Path engines](/release-notes/7-0/#path-engines).
* **JSONPath.** A second JSON path engine, `json-path-jayway`, evaluates [JSONPath](https://www.rfc-editor.org/rfc/rfc9535) with [Jayway JsonPath](https://github.com/json-path/JsonPath), such as `$.store.book[?(@.price < 10)].title`. See [JSONPath with Jayway](/release-notes/7-0/#jsonpath-with-jayway).
* **The HTTP client.** The JDK’s `java.net.http.HttpClient` sends the requests, from the new `http-client-jdk` module, which `rest-assured` depends on. Apache HttpClient 4, and with it httpmime, httpcore, commons-codec and commons-logging, is no longer a dependency of any REST Assured artifact ([#1862](https://github.com/rest-assured/rest-assured/issues/1862), [#1696](https://github.com/rest-assured/rest-assured/issues/1696)). Add `http-client-apache5` to send the requests with Apache HttpClient 5 instead, for example for NTLM or the `Host` header, which the JDK client can’t send. `httpClientConfig().client(..)` picks the client and `customizeClient(..)` customizes it. See [The HTTP client](/release-notes/7-0/#the-http-client).
* **Clients for parallel tests.** `RestAssured.useClient(..)` binds a `RestAssuredClient` to the thread of a test, so tests that run in parallel can each have their own settings and keep using plain `given()` ([#534](https://github.com/rest-assured/rest-assured/issues/534)). `given(client)` names a client for one request. `RestAssuredMockMvcClient` and `RestAssuredWebTestClientClient` do the same for Spring, and Kotlin gets `Given(client) { }` and Scala `Given(client)(..)`. See [Clients for parallel tests](/release-notes/7-0/#clients-for-parallel-tests).
* **Specifications.** Sending a request, or validating a response, no longer changes the specification, so one specification can be used from many threads ([#945](https://github.com/rest-assured/rest-assured/issues/945)). Specifications get the static settings, or those of the client, when they’re used, instead of copying the static settings when they’re built ([#1977](https://github.com/rest-assured/rest-assured/issues/1977)). See [Specifications no longer change when sent or validated](/release-notes/7-0/#specifications-no-longer-change-when-sent-or-validated) and [Where requests get their settings](/release-notes/7-0/#where-requests-get-their-settings).
* **Jackson 3.** `json-schema-validator` validates with the networknt json-schema-validator 3.0.9, which brings Jackson 3, and the java-json-tools validator is optional. `JsonPath` now prefers Jackson 3, like the object mapping, so `response.jsonPath().getObject(..)` and `response.as(..)` use the same Jackson. See [JSON Schema validation and Jackson 3](/release-notes/7-0/#json-schema-validation-and-jackson-3).
* **OAuth 1** no longer needs scribejava: REST Assured signs the requests itself. See [OAuth 1 without scribejava](/release-notes/7-0/#oauth-1-without-scribejava).
* **Removed APIs.** The APIs that take or return HttpClient 4 types, deprecated in 6.3.0, `HTTPBuilder`, the root path methods deprecated since 4.0.0, such as `root(..)`, and Jackson 1 support are removed. The OpenRewrite recipe `io.restassured.UpgradeToRestAssured7` changes most of this in your Java code. See [Removed APIs](/release-notes/7-0/#removed-apis) and [Let OpenRewrite do it](/release-notes/7-0/#let-openrewrite-do-it).
* **Logging.** The logging filters take their settings from the config of the request when they log, and every response log now hides the blacklisted headers, not only `then().log()`. See [Logging](/release-notes/7-0/#logging).
* **Dependencies.** Hamcrest 3.0, Groovy 4.0 to 6.x for the GPath modules, Jayway JsonPath 3.0.0 for `json-path-jayway`, and a Java 17 execution environment in OSGi. See [Other changes](/release-notes/7-0/#other-changes).

## Upgrading to 7.0

Start from REST Assured 6.3.0 without deprecation warnings, as [Preparing for REST Assured 7.0](/docs/upgrading/preparing-for-7-0/) describes. Then change the version to `7.0.0-RC1` and, if your tests use paths, add a path engine. Or [let OpenRewrite do it](/release-notes/7-0/#let-openrewrite-do-it). [Which module do I need](/docs/modules/) lists every module, what it’s for and how to keep their versions in line.

### Add a path engine

REST Assured 7.0.0 no longer brings `json-path` and `xml-path`, and so no longer brings Groovy either. If your tests use paths, such as `body("lotto.lottoId", equalTo(5))`, `path(..)`, `extract().path(..)` or `jsonPath()`, add GPath to your test dependencies. Your paths keep working unchanged:

```xml
<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>json-path-gpath</artifactId>
    <version>7.0.0-RC1</version>
    <scope>test</scope>
</dependency>
```

Gradle:

```kotlin
testImplementation("io.rest-assured:json-path-gpath:7.0.0-RC1")
```

Tests that use paths on XML or HTML, or call `xmlPath()` or `htmlPath()`, also need `xml-path-gpath`:

```xml
<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>xml-path-gpath</artifactId>
    <version>7.0.0-RC1</version>
    <scope>test</scope>
</dependency>
```

Gradle:

```kotlin
testImplementation("io.rest-assured:xml-path-gpath:7.0.0-RC1")
```

Without a path engine, the first path fails with a message that names the dependency to add:

```plaintext
java.lang.IllegalStateException: REST Assured can't evaluate the path "lotto.lottoId" because no JSON path engine is on the classpath.
```

`jsonPath()`, `xmlPath()` and `htmlPath()` fail the same way: “REST Assured can’t run jsonPath() because no JSON path engine is on the classpath.” See [Path engines](/release-notes/7-0/#path-engines) for what needs which engine. For JSON, the message also offers [JSONPath with Jayway](/release-notes/7-0/#jsonpath-with-jayway), which suits new tests written in JSONPath rather than the paths you have.

If you use `rest-assured-all`, you don’t need to do anything: it still contains `json-path-gpath` and `xml-path-gpath` and depends on Groovy.

### The new artifact names

`json-path` and `xml-path` are renamed `json-path-gpath` and `xml-path-gpath`, with the same packages and classes. If you depend on `json-path` or `xml-path` directly, switch to the new names. In 7.x the old names are relocation poms that resolve to the new ones at the same version, so a build that still uses them keeps working: Maven prints a warning, and Gradle resolves them silently. The old names are removed in REST Assured 8.0.0.

### Spring Boot 3

Spring Boot 3’s dependency management pins `rest-assured`, `rest-assured-common`, `json-path` and `xml-path` to REST Assured 5.5.x. Set the `rest-assured.version` property, so that all of them change together. Spring Boot 3.5 imports `rest-assured-bom` at that version, so the new modules, such as `json-path-gpath` and `http-client-apache5`, get it too:

```xml
<properties>
    <rest-assured.version>7.0.0-RC1</rest-assured.version>
</properties>
```

With Gradle and the Spring dependency management plugin: `ext["rest-assured.version"] = "7.0.0-RC1"` in `build.gradle.kts`. Without Spring Boot, import `io.rest-assured:rest-assured-bom` instead to keep the REST Assured artifacts on the same version, see [Keep the versions in line](/docs/modules/#keep-the-versions-in-line).

If you only change the version of `rest-assured`, Spring Boot 3 keeps `json-path` 5.5.x on the classpath. Its classes are in the same packages as those of `json-path-gpath`, so REST Assured stops with a message that tells you to replace it with `json-path-gpath` or to set `rest-assured.version`, instead of failing later with a `NoSuchMethodError`. Likewise, REST Assured now checks that `rest-assured-common`, `json-path-gpath` and `xml-path-gpath` have the same major and minor version as `rest-assured`, and fails with a message that names both versions. A different patch version only logs a warning.

### Spring Boot 4

Spring Boot 4 doesn’t manage REST Assured, so it neither pins its version nor reads a `rest-assured.version` property. Set the version on each REST Assured dependency, or import `rest-assured-bom` and leave the versions out:

```xml
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured-bom</artifactId>
            <version>7.0.0-RC1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
```

Gradle:

```kotlin
testImplementation(platform("io.rest-assured:rest-assured-bom:7.0.0-RC1"))
```

Spring Boot 4 manages some libraries that REST Assured’s modules use, and the versions it picks work: Groovy 5.0.x for `json-path-gpath` and `xml-path-gpath`, Jayway JsonPath 2.10.0 for `json-path-jayway`, Apache HttpClient 5.5.x (Spring Boot 4.0) or 5.6.x (Spring Boot 4.1) for `http-client-apache5`, and Kotlin 2.2 or later for the Kotlin extensions. It manages both Jackson 2 and Jackson 3. With both on the test classpath, REST Assured uses Jackson 3, see [JSON Schema validation and Jackson 3](/release-notes/7-0/#json-schema-validation-and-jackson-3).

### Let OpenRewrite do it

The `rest-assured-openrewrite` module contains the [OpenRewrite](https://docs.openrewrite.org) recipe `io.restassured.UpgradeToRestAssured7`. It:

* runs `io.restassured.MigrateRestAssured6Deprecations`, which moves your Java code from the APIs deprecated in 6.3.0 (see [Preparing for REST Assured 7.0](/docs/upgrading/preparing-for-7-0/)),
* removes the SSL port settings, renames the root path methods deprecated since REST Assured 4.0.0, such as `root(..)` to `rootPath(..)`, and replaces `ContextAuthenticationScheme` with `AuthenticationScheme` (see [Removed APIs](/release-notes/7-0/#removed-apis)),
* changes `json-path` and `xml-path` to `json-path-gpath` and `xml-path-gpath` in Maven and Gradle builds,
* upgrades the `io.rest-assured` dependencies to 7.0.0-RC1, including a version property such as `rest-assured.version`,
* and adds `json-path-gpath` where your Java code evaluates paths, such as `body("a.b", ..)`, `path(..)`, `rootPath(..)` or `jsonPath()`, and `xml-path-gpath` where it calls `xmlPath()` or `htmlPath()` or uses `XmlPath`. They get the scope (or Gradle configuration) and version of your REST Assured dependency, often `${rest-assured.version}`.

A path on an XML body looks just like a path on a JSON body, so the recipe can’t tell that a test needs `xml-path-gpath` for it. Add `xml-path-gpath` yourself if your tests use paths on XML or HTML. The recipe changes Java code only, so change Kotlin, Groovy and Scala code yourself.

With Maven, run this in your project:

```bash
mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
  -Drewrite.recipeArtifactCoordinates=io.rest-assured:rest-assured-openrewrite:7.0.0-RC1 \
  -Drewrite.activeRecipes=io.restassured.UpgradeToRestAssured7
```

With Gradle, add the OpenRewrite plugin and the recipe to `build.gradle.kts`:

```kotlin
plugins {
    id("org.openrewrite.rewrite") version "latest.release"
}


rewrite {
    activeRecipe("io.restassured.UpgradeToRestAssured7")
}


dependencies {
    rewrite("io.rest-assured:rest-assured-openrewrite:7.0.0-RC1")
}
```

Then run `gradle rewriteRun`. Review the changes with `git diff` before you commit them.

### Code that no longer compiles

* `jsonConfig().numberReturnType(JsonPathConfig.NumberReturnType.BIG_DECIMAL)`. `JsonConfig` no longer knows `json-path`’s enum. Use `io.restassured.common.config.NumberReturnType` instead.
* Storing the result of an `ObjectMapperConfig` factory getter, such as `jackson2ObjectMapperFactory()`, or of `JsonConfig.numberReturnType()`, in a variable of a `json-path` or `xml-path` type. They now return the `rest-assured-common` types in `io.restassured.common.mapper.factory` and `io.restassured.common.config`. The factories you set still work, since the old factory types extend the new ones.
* Jackson 1. `ObjectMapperType.JACKSON_1`, `JsonParserType.JACKSON_1`, `Jackson1ObjectMapperFactory`, `jackson1ObjectMapperFactory(..)` and `JsonPath.using(Jackson1ObjectMapperFactory)` are removed, and Jackson 1 on the classpath is no longer used for object mapping. Use Jackson 2 or 3.
* Code that calls `jsonPath()`, `xmlPath()` or `htmlPath()`, or uses `JsonPath` or `XmlPath`, needs `json-path-gpath` or `xml-path-gpath` to compile.
* Code that uses the APIs that take or return HttpClient 4 types, or `HTTPBuilder`. See [Removed APIs](/release-notes/7-0/#removed-apis).
* Code that uses HttpClient 4 classes itself, which REST Assured no longer brings. Add `org.apache.httpcomponents:httpclient` to your own build if you need them.
* The root path methods deprecated since REST Assured 4.0.0, such as `root(..)`, `appendRoot(..)` and `JsonPath.setRoot(..)`. Use `rootPath(..)`, `appendRootPath(..)` and `setRootPath(..)`, see [Removed APIs](/release-notes/7-0/#removed-apis).

The OpenRewrite recipe changes the first two, the root path methods, and the SSL and multipart methods among the removed APIs.

### Mocks of Response

`Response` still has `jsonPath()`, `xmlPath()` and `htmlPath()`, which return `JsonPath` and `XmlPath`. Code that never calls them runs without a path engine. But reflection over all the methods of `Response`, which is what Mockito’s `mock(Response.class)` and Spock’s `Mock(Response)` do, loads `JsonPath` and `XmlPath`. If you mock `Response`, keep `json-path-gpath` and `xml-path-gpath` on the test classpath.

## Path engines

REST Assured 7.0.0 has no path engine by default. There are two:

* GPath, the path syntax REST Assured has always used, such as `lotto.winners.winnerId`. It comes in two modules: `json-path-gpath` for JSON, and `xml-path-gpath` for XML and HTML. They depend on Groovy, which REST Assured itself no longer needs.
* JSONPath, such as `$.lotto.winners[*].winnerId`, with Jayway JsonPath, in `json-path-jayway`. JSON only, and without Groovy.

### GPath

Pick GPath if you’re upgrading from 6.x: your paths keep working unchanged.

| You use                                                                                                                                               | You need          |
| ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- |
| A path on a JSON body: `body("a.b", ..)`, `path(..)`, `extract().path(..)`, `rootPath(..)`                                                            | `json-path-gpath` |
| A path on an XML or HTML body                                                                                                                         | `xml-path-gpath`  |
| `jsonPath()` or `JsonPath`                                                                                                                            | `json-path-gpath` |
| `xmlPath()`, `htmlPath()` or `XmlPath`                                                                                                                | `xml-path-gpath`  |
| Anything else, such as `statusCode(..)`, headers, cookies, `body(containsString(..))`, `hasXPath(..)`, JSON schema validation or `as(Greeting.class)` | Nothing           |

The same goes for `RestAssuredMockMvc`, `RestAssuredWebTestClient` and the Kotlin and Scala extensions.

### Groovy versions

`json-path-gpath`, `xml-path-gpath` and `rest-assured-all` are built with Groovy 5.1.3 and depend on it. They work with Groovy 4.0, 5.x and 6.x, which CI tests (4.0.33, 5.0.8 and 6.0.0), so the Groovy version that Spring Boot manages works as it is: 4.0.x in Spring Boot 3.5, and 5.0.x in Spring Boot 4. They’re built with Groovy 5 because code compiled by Groovy 6 doesn’t run on Groovy 4 or 5. In OSGi, the bundles import Groovy from 4.0 up to, but not including, 7.0.

With Groovy 6, two things differ:

* A JSON body that is just a string with an invalid escape, such as `"\x"`, fails to parse, with Groovy’s `groovy.json.JsonException` as the cause, instead of giving `null`. The same escape inside an object or an array fails with every Groovy version.
* An XML body nested more than 1000 elements deep fails, unless the system property `jdk.xml.maxElementDepth` is set.

### JSONPath with Jayway

`json-path-jayway` evaluates JSON paths in `body(..)`, `path(..)`, `extract().path(..)`, `rootPath(..)`, `equalToPath(..)` and `ResponseAwareMatcher`s with [Jayway JsonPath](https://github.com/json-path/JsonPath):

```xml
<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>json-path-jayway</artifactId>
    <version>7.0.0-RC1</version>
    <scope>test</scope>
</dependency>
```

Gradle:

```kotlin
testImplementation("io.rest-assured:json-path-jayway:7.0.0-RC1")
```

```java
get("/lotto").then().body("$.lotto.winners[*].winnerId", hasItems(23, 54));
get("/store").then().body("$.store.book[?(@.price < 10)].title", hasItems("Sayings of the Century", "Moby Dick"));
```

A path may leave out the leading `$.`, so `lotto.lottoId` works too. `jsonPath()` and `JsonPath` are GPath. With Jayway, use `JaywayPath.from(response.asString())`, which returns Jayway’s `DocumentContext` configured like the module:

```java
List<Integer> winnerIds = JaywayPath.from(get("/lotto").asString()).read("$.lotto.winners[*].winnerId");
```

The module needs Jayway JsonPath 2.8.0 or later, and brings 3.0.0, which needs Java 17. A version that your build manages, such as Spring Boot 4’s 2.10.0, keeps working. If dependency management sets a version older than 2.8.0, the module fails with a message that says so. Jayway logs through SLF4J, so without an SLF4J provider on the classpath you see SLF4J’s three-line “No SLF4J providers were found” warning once. Add a provider such as `slf4j-simple` or `logback-classic` in test scope to replace it with real logging.

### Picking the engine

With only one of `json-path-gpath` and `json-path-jayway` on the classpath, REST Assured uses that one. With both, it uses GPath and logs once, at INFO level through the `io.restassured` `System.Logger`, that it does. To use Jayway, set `JsonConfig.pathEngine(..)`:

```java
// For all tests
RestAssured.config = RestAssured.config().jsonConfig(jsonConfig().pathEngine(JsonPathEngine.JAYWAY));


// For one request
given().config(config().jsonConfig(jsonConfig().pathEngine(JsonPathEngine.JAYWAY))). ..


// For a request or response specification
RequestSpecification requestSpec = new RequestSpecBuilder().setConfig(config().jsonConfig(jsonConfig().pathEngine(JsonPathEngine.JAYWAY))).build();
ResponseSpecification responseSpec = new ResponseSpecBuilder().setConfig(config().jsonConfig(jsonConfig().pathEngine(JsonPathEngine.JAYWAY)))
        .expectBody("$.lotto.winners[*].winnerId", hasItems(23, 54)).build();
```

A response specification’s config outranks the request’s, which outranks `RestAssured.config`. The configs are merged per type, so the `JsonConfig` of a response specification replaces the request’s whole `JsonConfig`, including a `numberReturnType(..)` set globally. That lets one test class move to Jayway while the others keep GPath. `pathEngine(..)` with an engine whose module isn’t on the classpath fails with “JsonConfig.pathEngine is JAYWAY but io.rest-assured:json-path-jayway isn’t on the classpath.”

The same goes for `RestAssuredMockMvc` and `RestAssuredWebTestClient`, with their own `config`.

### How Jayway differs from GPath

* **Lists.** GPath’s `lotto.winners.winnerId` collects `winnerId` from each element of the `winners` list. In JSONPath that’s `lotto.winners[*].winnerId` or `$..winnerId`. With Jayway, `lotto.winners.winnerId` fails with “JSONPath (Jayway) can’t evaluate lotto.winners.winnerId: lotto.winners is a list, so use lotto.winners\[\*].winnerId.” When GPath isn’t on the classpath, the message also suggests adding `json-path-gpath`, in case the path comes from a 6.x test.
* **Missing values.** A missing property gives `null` in both engines, so `body("missing", nullValue())` works with both. A filter or wildcard that matches nothing gives an empty list in Jayway.
* **GPath syntax.** Closures such as `findAll { it.price < 10 }`, GPath methods such as `unique()` or `collect { }`, and a quoted key after a dot, such as `store.'my-key'`, aren’t JSONPath and fail as an invalid path. Use a filter, such as `[?(@.price < 10)]`, and bracket notation, such as `store['my-key']`. Jayway has `length()`, and accepts `size()`. It applies a function after `[*]`, such as `sum()`, to each element, so aggregate over a deep scan instead, such as `$..price.sum()`.
* **Keys that start with `$`.** A leading `$schema` is the key `$schema`, as in GPath. Use `$['$schema']` to make it explicit.
* **Numbers** follow `JsonConfig.numberReturnType(..)` as with GPath: `FLOAT_AND_DOUBLE` by default, and `BIG_DECIMAL` keeps the scale, so `12.50` stays `12.50`.
* **Invalid JSON.** Jayway’s parser is strict. GPath reads some invalid JSON that Jayway fails on: a trailing comma (`[1,]`), a leading zero (`[01]`), a raw control character in a string, and anything after the document (`{"a":1}garbage`).

### OSGi

The REST Assured bundles require a Java 17 execution environment (`osgi.ee` JavaSE 17), which every Java 17 framework provides.

The `rest-assured` bundle resolves without the path engine bundles, Groovy and SPI Fly. Install the `json-path-gpath` and `xml-path-gpath` bundles (and Groovy 4.0 to 6.x), or the `json-path-jayway` bundle, to use paths. They’re found even if they’re installed after `rest-assured`.

`json-path-jayway` needs the Jayway JsonPath bundle (`json-path`) and what it requires: json-smart (`net.minidev.json-smart`), accessors-smart (`net.minidev.accessors-smart`), ASM (`org.objectweb.asm`) and slf4j-api (`slf4j.api`). With Jayway JsonPath 2.9.0 and later, that’s slf4j-api 2, which in turn requires an SLF4J provider bundle, such as slf4j-simple (`slf4j.simple`), and an OSGi ServiceLoader Mediator, such as Apache Aries SPI Fly (`org.apache.aries.spifly.dynamic.bundle`). Jayway JsonPath 2.8.0 needs slf4j-api 1.7 and an SLF4J 1.7 binding bundle, such as slf4j-simple 1.7, but no SPI Fly. Jayway JsonPath 3.0.0, which `json-path-jayway` brings, also requires the Jackson 3 bundles `tools.jackson.core.jackson-core` and `tools.jackson.core.jackson-databind`, and `com.fasterxml.jackson.core.jackson-annotations` 2.x, which jackson-databind 3 imports.

## The HTTP client

REST Assured sends the requests with an HTTP client module. There are two:

* `http-client-jdk` uses the JDK’s `java.net.http.HttpClient` and needs nothing else. `rest-assured` depends on it, so you don’t have to add anything.
* `http-client-apache5` uses Apache HttpClient 5. It behaves more like Apache HttpClient 4, which REST Assured 6 used, and does some things that the JDK client can’t. You add it yourself.

### Apache HttpClient 5

To send the requests with Apache HttpClient 5, add `http-client-apache5` to your test dependencies:

```xml
<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>http-client-apache5</artifactId>
    <version>7.0.0-RC1</version>
    <scope>test</scope>
</dependency>
```

Gradle:

```kotlin
testImplementation("io.rest-assured:http-client-apache5:7.0.0-RC1")
```

REST Assured then uses it instead of the JDK client, unless you pick another client (see [Picking the client](/release-notes/7-0/#picking-the-client)). `rest-assured-bom` manages its version. Like the other REST Assured modules, it must have the same major and minor version as `rest-assured`, or the request fails with a message that names both versions.

Like Apache HttpClient 4 in REST Assured 6, it creates a client, and so a new connection, for each request, gives the response headers in the order and case the server sent them, with the server’s reason phrase, and sends several `Cookie` headers as they are. It sends the `Host`, `Connection`, `Upgrade` and `Expect` headers of a request, answers NTLM challenges (`auth().ntlm(..)`), connects to a proxy with the scheme `https` over TLS, with the SSL settings of the request, and logs in to a proxy with Basic for an HTTPS request. The JDK client can’t do those. See [How Apache HttpClient 5 differs from the JDK client](/release-notes/7-0/#how-apache-httpclient-5-differs-from-the-jdk-client).

REST Assured follows the redirects, answers the Basic and Digest challenges, keeps the cookies and decodes gzip and deflate itself, so HttpClient 5 does none of that, and it retries nothing. Its timeouts are infinite unless `httpClientConfig()` sets them. Its read timeout covers each read of the body too, and its connect timeout also covers the TLS handshake of a new connection.

The module supports Apache HttpClient 5.2.1 and later, with HttpCore 5.2 and later, and depends on 5.6.5. If dependency management, such as a BOM, sets an older version, or HttpClient 5 is excluded, the request fails with a message that says what’s missing or too old, for example:

```plaintext
java.lang.IllegalStateException: http-client-apache5 needs Apache HttpClient 5.2.1 or later, but 5.1.3 is on the classpath, probably set by a BOM such as Spring Boot's.
```

REST Assured doesn’t use the JDK client instead, because you added `http-client-apache5` on purpose. HttpClient 5 brings `httpcore5-h2`, which it uses for HTTP/1.1 too, so don’t exclude it. Without it, the request fails with a message that says so.

Apache HttpClient 5 logs through SLF4J, which prints a warning when there’s no SLF4J backend. To get rid of it, add a backend that matches the `slf4j-api` version on your test classpath, such as `slf4j-simple` or `slf4j-nop`, in test scope. HttpClient 5 brings `slf4j-api` 1.7.36, unless your build manages another version.

If the application under test runs in the same JVM as the tests, for example with `@SpringBootTest`, adding `http-client-apache5` also puts Apache HttpClient 5 on its classpath. Spring’s `RestClient` and `RestTemplate` then pick HttpClient 5 too, and HttpClient 5 retries a 503 or 429 response with a `Retry-After` header once by default. REST Assured’s own requests are never retried, and REST Assured leaves the application’s client alone. To keep the application on the client it had, set its request factory, for example to `JdkClientHttpRequestFactory`, or set the Spring Boot property that picks it (`spring.http.client.factory=jdk` in recent versions).

### Picking the client

Without a choice, REST Assured uses `http-client-apache5` if it’s on the classpath, and otherwise `http-client-jdk`. In full, REST Assured uses the first of these:

1. The client that `httpClientConfig().client(..)` picks, in the config of the request.
2. The client that the system property `restassured.http.client` names.
3. `http-client-apache5`, if it’s on the classpath.
4. `http-client-jdk`.

It logs the client it uses once through the `io.restassured` `System.Logger`: at DEBUG level for `http-client-jdk` when nothing chose it, and otherwise at INFO level, for example “REST Assured uses the HTTP client from io.rest-assured:http-client-apache5 (Apache HttpClient 5.6.5), found on the classpath. To pin a client, use httpClientConfig().client(..) or the system property restassured.http.client.”

Pick a client for all requests, or for one:

```java
RestAssured.config = RestAssured.config().httpClient(httpClientConfig().client(HttpClientType.JDK));
given().config(RestAssured.config().httpClient(httpClientConfig().client(HttpClientType.APACHE5))). ..
```

Or with a system property, for every request that doesn’t pick one in code: `-Drestassured.http.client=jdk` or `-Drestassured.http.client=apache5`. Another library can provide a client by implementing `io.restassured.common.spi.http.HttpClientProvider`. REST Assured only uses such a client when you name it, with `client("name")`, `client(ProviderClass.class)` or the system property.

A client that you pick but that isn’t on the classpath fails the request with a message that says which dependency to add. If you exclude `http-client-jdk` and have no other client, the request fails with “REST Assured found no HTTP client module. Add io.rest-assured:http-client-jdk (no other dependencies) or io.rest-assured:http-client-apache5 to your test dependencies.”

### Customizing the client

The settings of `httpClientConfig()`, such as `connectTimeout(..)`, `readTimeout(..)` and `expectContinue(..)`, work with every client (see [HTTP Client Config](/docs/usage/configuration/#http-client-config)). For anything else, customize the builder of the client’s library:

```java
RestAssured.config = RestAssured.config().httpClient(httpClientConfig()
        .customizeClient(java.net.http.HttpClient.Builder.class, builder -> builder.executor(executor)));
```

The customizers run in the order you add them. A customizer for the builder of another client, such as Apache HttpClient 5’s `HttpClientBuilder` while the JDK client is used, fails the request with a message, for example “A customizer for org.apache.hc.client5.http.impl.classic.HttpClientBuilder is configured, but REST Assured uses the JDK HTTP client.”

The JDK client is shared by all requests with the same settings, so they share its connections too. `connectionConfig().closeIdleConnectionsAfterEachResponse()` gives each request a new client instead.

For Apache HttpClient 5, customize its `org.apache.hc.client5.http.impl.classic.HttpClientBuilder`. The customizers get the builder after REST Assured has set it up:

```java
RestAssured.config = RestAssured.config().httpClient(httpClientConfig()
        .customizeClient(HttpClientBuilder.class, builder -> builder.addRequestInterceptorFirst(
                (request, entity, context) -> request.addHeader("X-Trace-Id", traceId))));
```

REST Assured can’t read what a customizer set on the builder, so this is what each setting does:

| A customizer that calls                                                                                                                                                                                  | Has this effect                                                                                                                                                                                                                                                                                             |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The interceptors, such as `addRequestInterceptorFirst(..)`, and `setUserAgent(..)`, `setDefaultHeaders(..)`, `setKeepAliveStrategy(..)`, `setConnectionReuseStrategy(..)` and `evictIdleConnections(..)` | They apply                                                                                                                                                                                                                                                                                                  |
| `setConnectionManager(..)`                                                                                                                                                                               | Replaces REST Assured’s connection manager, together with its SSL settings, its connect timeout and its pool, which has no limit. It’s the way to limit the connections, and `connectionRequestTimeout(..)` then limits the wait for one. `closeIdleConnectionsAfterEachResponseAfter(..)` doesn’t reach it |
| `setDefaultAuthSchemeRegistry(..)`                                                                                                                                                                       | Replaces REST Assured’s registry. Keep `"ntlm"` (`NTLMSchemeFactory.INSTANCE`) in it for `auth().ntlm(..)`, and `"basic"` and `"digest"` for the login to a proxy                                                                                                                                           |
| `useSystemProperties()`                                                                                                                                                                                  | HttpClient reads the system properties `http.agent`, for the `User-Agent`, and `http.keepAlive`, for whether connections are kept, and nothing else                                                                                                                                                         |
| Retries, redirects, cookies, decompression, `setTargetAuthenticationStrategy(..)` and `setRoutePlanner(..)`                                                                                              | No effect. REST Assured switches them back after the customizers, since it does them or decides them itself                                                                                                                                                                                                 |
| `setDefaultRequestConfig(..)`, `setDefaultCredentialsProvider(..)` and `setProxy(..)`                                                                                                                    | No effect, since each request has REST Assured’s config, credentials and route. Use `httpClientConfig()`, `auth()` and `proxy(..)` instead                                                                                                                                                                  |

A client shuts its connection manager down when it’s closed, and REST Assured closes the client of a request after its response, unless `reuseHttpClientInstance()` keeps the client. So create the connection manager inside the customizer, or create it once, call `setConnectionManagerShared(true)` and close it yourself. A limit that holds across requests needs `reuseHttpClientInstance()` or such a shared connection manager:

```java
RestAssured.config = RestAssured.config().httpClient(httpClientConfig()
        .reuseHttpClientInstance()
        .customizeClient(HttpClientBuilder.class, builder -> builder.setConnectionManager(
                PoolingHttpClientConnectionManagerBuilder.create().setMaxConnTotal(10).build())));
```

A request whose connection manager has been shut down fails with an `IllegalStateException` that says so.

### How the JDK client differs from HttpClient 4

| What                                                                       | HttpClient 4                                                                         | JDK client                                                                                                                                                                                                                                          |
| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Response header names, in `headers()`, the logs and assertion messages     | As the server sent them, in its order                                                | In lower case and sorted by name. Assertions on headers ignore the case of the name, so they pass                                                                                                                                                   |
| Status line                                                                | The server’s reason phrase                                                           | The standard reason phrase of the status code, such as `HTTP/1.1 404 Not Found`, whatever the server sent                                                                                                                                           |
| `User-Agent`                                                               | `Apache-HttpClient/4.5.14`                                                           | `Java-http-client/<Java version>`. Set the header for another                                                                                                                                                                                       |
| Connections                                                                | A new connection for each request                                                    | Requests with the same settings share one client, and reuse its connections. A response whose body is never read keeps its connection until it’s garbage collected. The `javax.net.ssl` trust store properties are read when that client is created |
| Read timeout (`readTimeout(..)`)                                           | Each pause while reading                                                             | Connecting and waiting for the response headers, not reading the body. It’s a `ConnectionTimeoutException` when it fires before the connection is up                                                                                                |
| `connectionRequestTimeout(..)`                                             | The wait for a pooled connection                                                     | No effect, since the JDK client doesn’t limit its connections. A warning is logged once                                                                                                                                                             |
| `closeIdleConnectionsAfterEachResponseAfter(..)` with an idle time above 0 | Closes the connections that were idle for longer after each response                 | No effect, since the JDK client closes idle connections after a time that it decides. A warning is logged once. See `jdk.httpclient.keepalive.timeout` under [JVM properties](/release-notes/7-0/#jvm-properties)                                   |
| Retries                                                                    | Its retry handler                                                                    | The JDK client retries when connecting fails, or when a reused connection turns out to be closed. See [JVM properties](/release-notes/7-0/#jvm-properties)                                                                                          |
| Closing a partly read body stream                                          | Reads the rest of the body                                                           | Closes the connection                                                                                                                                                                                                                               |
| Several `Cookie` headers                                                   | Sent as they are                                                                     | Joined into one                                                                                                                                                                                                                                     |
| A proxy’s Basic login request (407)                                        | Answered once, then the login is sent up front, also after a redirect or a challenge | Answered for each request that REST Assured sends, also after a redirect or a challenge. A `Proxy-Authorization` header is only sent to the proxy                                                                                                   |
| A proxy that refuses an HTTPS tunnel                                       | Its response                                                                         | An `IOException`                                                                                                                                                                                                                                    |
| `OPTIONS` and `TRACE` without a body, and `HEAD` on Java 17                | No `Content-Length`                                                                  | `Content-Length: 0`                                                                                                                                                                                                                                 |
| `Expect: 100-continue` and a chunked body                                  | As written here                                                                      | `Expect: 100-Continue` and `Transfer-encoding: chunked`                                                                                                                                                                                             |
| A host name that a `HostnameVerifier` rejects                              | `SSLPeerUnverifiedException`                                                         | `SSLException` caused by the `SSLPeerUnverifiedException`                                                                                                                                                                                           |
| A body cut short                                                           | `ConnectionClosedException`                                                          | A plain `IOException`                                                                                                                                                                                                                               |
| HTTP version                                                               | HTTP/1.1                                                                             | HTTP/1.1                                                                                                                                                                                                                                            |

[]()

### JDK client limitations

The JDK client can’t:

* send the `Host`, `Connection` or `Upgrade` headers, unless the JVM runs with `-Djdk.httpclient.allowRestrictedHeaders` naming them,
* send an `Expect` header other than `100-continue`,
* do NTLM authentication (`auth().ntlm(..)`),
* use TLS to the proxy (`ProxySpecification.withScheme("https")`),
* log in to a proxy with Basic for an HTTPS request, unless the JVM runs with `-Djdk.http.auth.tunneling.disabledSchemes=` (an empty value),
* send with an `Authenticator` set through `customizeClient(..)`.

Those fail the request with an `UnsupportedOperationException` whose message says what to do, for example “The JDK HTTP client (the default module, http-client-jdk) can’t send a Host header. Add io.rest-assured:http-client-apache5 to your test dependencies and REST Assured uses it, or start the JVM with -Djdk.httpclient.allowRestrictedHeaders=host.” [Apache HttpClient 5](/release-notes/7-0/#apache-httpclient-5) can do all of them but the last.

### How Apache HttpClient 5 differs from the JDK client

| What                                                                       | JDK client                                                                              | Apache HttpClient 5                                                                                                                                                                                                                                                                                                                        |
| -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Response header names, in `headers()`, the logs and assertion messages     | In lower case and sorted by name                                                        | As the server sent them, in its order, as with HttpClient 4                                                                                                                                                                                                                                                                                |
| Status line                                                                | The standard reason phrase of the status code                                           | The server’s reason phrase, as with HttpClient 4                                                                                                                                                                                                                                                                                           |
| `User-Agent`                                                               | `Java-http-client/<Java version>`                                                       | `Apache-HttpClient/<version> (Java/<Java version>)`. HttpClient 4 sent `Apache-HttpClient/4.5.14`                                                                                                                                                                                                                                          |
| Connections                                                                | Requests with the same settings share one client, and reuse its connections             | A new client, and so a new connection, for each request, as with HttpClient 4. The client lasts across the redirects of the request and the answers to its login challenges, and is closed after the response. With `reuseHttpClientInstance()`, the requests share one client, which keeps its connections, with no limit on their number |
| Read timeout (`readTimeout(..)`)                                           | Connecting and waiting for the response headers, not reading the body                   | Each read, also of the body after the request, as with HttpClient 4                                                                                                                                                                                                                                                                        |
| `connectionRequestTimeout(..)`                                             | No effect. A warning is logged once                                                     | Only has an effect when a customizer sets a connection manager that limits the connections                                                                                                                                                                                                                                                 |
| `closeIdleConnectionsAfterEachResponseAfter(..)` with an idle time above 0 | No effect. A warning is logged once                                                     | Closes the connections that were idle for longer after each response, as with HttpClient 4, for a client kept with `reuseHttpClientInstance()`. Without it, the client of each request is closed after its response anyway                                                                                                                 |
| Retries                                                                    | When connecting fails, or when a reused connection turns out to be closed               | None                                                                                                                                                                                                                                                                                                                                       |
| Closing a partly read body stream                                          | Closes the connection                                                                   | Drops the connection, so a client kept with `reuseHttpClientInstance()` sends the next request on a new connection, as it does after a response whose body is never read. HttpClient 4 read the rest of the body first                                                                                                                     |
| Several `Cookie` headers                                                   | Joined into one                                                                         | Sent as they are, as with HttpClient 4                                                                                                                                                                                                                                                                                                     |
| A proxy’s Basic login request (407)                                        | Answered for each request that REST Assured sends, also after a redirect or a challenge | Answered once, then the login is sent with every later request of the client, as with HttpClient 4                                                                                                                                                                                                                                         |
| A proxy that refuses an HTTPS tunnel                                       | An `IOException`                                                                        | Its response with HttpClient 5.4 and later, as with HttpClient 4. A `ClientProtocolException` with HttpClient 5.2 and 5.3                                                                                                                                                                                                                  |
| `OPTIONS` and `TRACE` without a body, and `HEAD` on Java 17                | `Content-Length: 0`                                                                     | No `Content-Length`, as with HttpClient 4                                                                                                                                                                                                                                                                                                  |
| A custom method without a body, such as `PURGE`                            | `Content-Length: 0`, as with HttpClient 4                                               | No `Content-Length`                                                                                                                                                                                                                                                                                                                        |
| A host name that a `HostnameVerifier` rejects                              | `SSLException` caused by the `SSLPeerUnverifiedException`                               | `SSLPeerUnverifiedException`, as with HttpClient 4                                                                                                                                                                                                                                                                                         |
| A server that rejects a missing client certificate, with TLS 1.3           | `SSLException`                                                                          | `SSLException`, or a `SocketException` (broken pipe), depending on timing                                                                                                                                                                                                                                                                  |
| HTTP version                                                               | HTTP/1.1                                                                                | HTTP/1.1                                                                                                                                                                                                                                                                                                                                   |

Apache HttpClient 5 also does what [the JDK client can’t](/release-notes/7-0/#jdk-client-limitations), apart from the `Authenticator`: it sends the `Host`, `Connection` and `Upgrade` headers and any `Expect` header as given, answers NTLM challenges, connects to a proxy with the scheme `https` over TLS, with the SSL settings of the request, and logs in to a proxy with Basic for an HTTPS request. NTLM is deprecated in HttpClient 5 and may be removed in HttpClient 6.

### What Apache HttpClient 5 can’t do

Apache HttpClient 5 can’t:

* send a body with a `TRACE` request. It fails with an `UnsupportedOperationException` whose message says what to do, for example “Apache HttpClient 5 (found on the classpath, http-client-apache5) can’t send a body with a TRACE request. Choose another client with httpClientConfig().client(..).”
* send a `TRACE` request with an `Authorization` or `Cookie` header, with HttpClient 5.4 and later,
* send an `OPTIONS` request with a body but without a `Content-Type`, with HttpClient 5.4 and later (HttpCore 5.3 and later).

The last two fail with HttpClient’s `ClientProtocolException`, whose cause names the rule, such as “TRACE request MUST NOT contain an Authorization header.” or “OPTIONS request must have Content-Type header”. The JDK client, and HttpClient 4 in REST Assured 6, send all three.

Like the JDK client, it sends HTTP/1.1 only.

### Content-Length and Transfer-Encoding headers

REST Assured sends the length of the body itself, as a `Content-Length` header, or chunked when the length is unknown. A request that sets a `Content-Length` or `Transfer-Encoding` header, for example with `header("Content-Length", ..)`, fails with an `UnsupportedOperationException`, whichever client sends it:

```plaintext
java.lang.UnsupportedOperationException: REST Assured can't send a Content-Length header that the request sets. Remove the header: REST Assured sends the length of the body itself, as a Content-Length header, or chunked when the length is unknown.
```

Remove the header. No client can send these headers, so changing the client doesn’t help. In REST Assured 6, Apache HttpClient 4 refused them on a request with a body, with a `ClientProtocolException` (“Content-Length header already present”), but sent them as given on a `GET`, `HEAD`, `DELETE`, `OPTIONS` or `TRACE` request without a body. So, for example, `given().header("Content-Length", "0").delete(..)` worked in 6.x and now fails.

### JVM properties

The JDK reads these system properties for the whole JVM, most of them once, so set them where the JVM that runs the tests starts, for example in the `argLine` of the Maven Surefire plugin or `jvmArgs` of a Gradle `Test` task:

| Property                                                        | Effect                                                                                                           |
| --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `jdk.httpclient.allowRestrictedHeaders=host,connection,upgrade` | Lets requests set the headers it names                                                                           |
| `jdk.http.auth.tunneling.disabledSchemes=` (an empty value)     | Allows a Basic proxy login for HTTPS requests, which the JDK turns off by default                                |
| `jdk.httpclient.disableRetryConnect=true`                       | Doesn’t retry when connecting fails                                                                              |
| `jdk.httpclient.enableAllMethodRetry=true`                      | Also retries requests that aren’t idempotent, such as `POST`, on a reused connection that turns out to be closed |
| `jdk.httpclient.keepalive.timeout=<seconds>`                    | How long an idle connection is kept                                                                              |

For example with Maven:

```xml
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <argLine>-Djdk.httpclient.allowRestrictedHeaders=host</argLine>
    </configuration>
</plugin>
```

### Exceptions

* A connect timeout throws `io.restassured.common.exception.ConnectionTimeoutException`, and a read timeout `io.restassured.common.exception.ResponseTimeoutException`. With Apache HttpClient 5, a timeout while waiting for a pooled connection throws a `ConnectionTimeoutException` too, and the read timeout also covers reading the body after the request, such as in `asString()`. The JDK client supports neither (see [the table above](/release-notes/7-0/#how-the-jdk-client-differs-from-httpclient-4)). Both exceptions are `SocketTimeoutException`s. Before, HttpClient 4 threw its `ConnectTimeoutException` or `ConnectionPoolTimeoutException`, which aren’t, or a plain `SocketTimeoutException`.
* A host that can’t be resolved still throws `java.net.UnknownHostException`. With the JDK client its cause is the client’s `ConnectException`. With Apache HttpClient 5 it has no cause and the same message as in 6.x.
* A refused connection throws a `java.net.ConnectException`: the JDK client’s own, or Apache HttpClient 5’s `org.apache.hc.client5.http.HttpHostConnectException`.
* Other errors of Apache HttpClient 5 are its own `IOException`s, such as `org.apache.hc.client5.http.ClientProtocolException` (see [What Apache HttpClient 5 can’t do](/release-notes/7-0/#what-apache-httpclient-5-cant-do)).
* More redirects than `redirectConfig().maxRedirects(..)` allows throw `io.restassured.common.exception.TooManyRedirectsException`, and a circular redirect, a relative `Location` rejected by `rejectRelativeRedirect(true)`, an invalid `Location` and a redirect without a `Location` throw `java.net.ProtocolException`, with the same messages as before. Before, they were HttpClient 4’s `ClientProtocolException`s. `TooManyRedirectsException` is a `ProtocolException`.
* `io.restassured.internal.http.HttpResponseException` and `ResponseParseException` extend `java.io.IOException` instead of HttpClient 4’s `HttpResponseException`. `HttpResponseException.getResponse()` and the constructors that took an `HttpResponseDecorator` are removed.

Code that catches `IOException` keeps working.

### Removed APIs

These APIs were deprecated in 6.3.0 (see [Preparing for REST Assured 7.0](/docs/upgrading/preparing-for-7-0/#methods-that-take-httpclient-4-types)) and are removed:

| Removed                                                                                                                    | Use instead                                                                                                                                                                                                                                                                |
| -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SSLConfig.sslSocketFactory(..)`, `getSSLSocketFactory()`, `x509HostnameVerifier(..)` and `getX509HostnameVerifier()`      | `sslContext(..)`, `getSSLContext()`, `hostnameVerifier(..)` and `getHostnameVerifier()`                                                                                                                                                                                    |
| `CertificateAuthSettings.sslSocketFactory(..)`, `x509HostnameVerifier(..)` and their getters                               | `sslContext(..)`, `hostnameVerifier(..)` and their getters                                                                                                                                                                                                                 |
| `CertAuthScheme.setSslSocketFactory(..)`, `setX509HostnameVerifier(..)` and their getters                                  | `setSslContext(..)`, `setHostnameVerifier(..)` and their getters                                                                                                                                                                                                           |
| `HttpClientConfig.httpMultipartMode(..)` and `httpMultipartMode()`                                                         | `multipartMode(..)` and `multipartMode()`                                                                                                                                                                                                                                  |
| `HttpClientConfig.HttpClientFactory`, `httpClientFactory(..)`, `httpClientInstance()` and `usesDefaultHttpClientFactory()` | The settings of `httpClientConfig()`, or [`customizeClient(..)`](/release-notes/7-0/#customizing-the-client). `redirectConfig().followRedirectsForAllMethods(true)` instead of `LaxRedirectStrategy`, and `keepResponseCookies(true)` instead of the client’s cookie store |
| `QueryableRequestSpecification.getHttpClient()`                                                                            | Nothing                                                                                                                                                                                                                                                                    |
| `CookieFilter.getCookieStore()` and `extractEffectiveUriFromContext(..)`                                                   | `getCookies()` and `addCookie(..)`                                                                                                                                                                                                                                         |
| `authenticate(HTTPBuilder)` of the authentication schemes, and `io.restassured.internal.http.HTTPBuilder`                  | `authenticate(AuthenticationContext)`                                                                                                                                                                                                                                      |

`SSLConfig.port(..)` and `CertificateAuthSettings.port(..)`, with their `getPort()`, `CertAuthScheme.getPort()` and `setPort(..)`, and the port argument of `AuthenticationContext.certificate(..)` are removed too, without a deprecation first, because they never had an effect: an https URL without a port always went to port 443. Set the port in the URI, such as `https://localhost:8443`, or with `port(..)`. The OpenRewrite recipe removes the calls.

The root path methods deprecated since REST Assured 4.0.0 are removed too:

| Removed                                          | Use instead          |
| ------------------------------------------------ | -------------------- |
| `root(..)`                                       | `rootPath(..)`       |
| `noRoot()`                                       | `noRootPath()`       |
| `appendRoot(..)`                                 | `appendRootPath(..)` |
| `detachRoot(..)`                                 | `detachRootPath(..)` |
| `JsonPath.setRoot(..)` and `XmlPath.setRoot(..)` | `setRootPath(..)`    |

The first four are methods of `then()`, `expect()` and `ResponseSpecification`, and of the validatable responses of `RestAssuredMockMvc` and `RestAssuredWebTestClient`. `expect().appendRoot(path)` detached the path instead of appending it, so code that used it now gets what `appendRootPath(path)` does. The OpenRewrite recipe renames the calls.

`AuthenticationScheme` now has the single method `authenticate(AuthenticationContext)`, so a lambda can be one:

```java
RestAssured.authentication = context -> context.header("X-Api-Key", apiKey);
```

`ContextAuthenticationScheme` adds nothing to it any more and is deprecated. It’s kept until REST Assured 8.0.0, so that code written for 6.3.0 still compiles, and the OpenRewrite recipe replaces it with `AuthenticationScheme`. A class that extends one of REST Assured’s schemes and only overrides `authenticate(HTTPBuilder)` must override `authenticate(AuthenticationContext)` instead. A scheme compiled against REST Assured 6 that doesn’t implement `authenticate(AuthenticationContext)`, for example from a shared test jar, fails the request with an `IllegalStateException` that names its class.

The [OpenRewrite recipe](/release-notes/7-0/#let-openrewrite-do-it) changes the SSL and multipart methods and the root path methods, replaces `ContextAuthenticationScheme`, and marks the authentication schemes to change.

### HttpClient 4 parameters

The HttpClient 4 parameters set with `httpClientConfig().setParam(..)` and the other deprecated parameter methods still work for the keys that have a replacement, listed in [Preparing for REST Assured 7.0](/docs/upgrading/preparing-for-7-0/#httpclient-4-parameters), until 8.0.0. Any other key fails the request with an `IllegalArgumentException` that lists the settings to use instead, since REST Assured would otherwise drop it silently. The four redirect parameters, such as `http.protocol.handle-redirects`, have never had an effect, since the `RedirectConfig` outranks them, and log a warning once.

### OSGi and the HTTP client

The `rest-assured` bundle requires the `http-client-jdk` bundle (`io.rest-assured:http-client-jdk`), and no longer needs the HttpClient 4 bundles (`httpclient`, `httpmime` and `httpcore`). It imports `http-client-apache5` as optional, also dynamically, so that bundle is found when it’s installed. The clients of other libraries are found with `ServiceLoader`, which in OSGi needs a ServiceLoader Mediator, such as Apache Aries SPI Fly.

`http-client-apache5` is a bundle. HttpClient 5’s jars aren’t, so install them wrapped, for example with Pax URL’s `wrap:` or with bnd: `httpclient5`, `httpcore5` and `httpcore5-h2`. They import each other and SLF4J, and what HttpClient 5 uses only when it’s there, such as Conscrypt and Brotli, should be imported as optional. Also install `slf4j-api` and an SLF4J provider, such as `slf4j-simple`, and with SLF4J 2 an OSGi ServiceLoader Mediator, such as SPI Fly. Without them, the `http-client-apache5` bundle doesn’t resolve, and REST Assured uses the JDK client.

## Clients for parallel tests

The static settings of `RestAssured` are shared by all threads. Tests that run in parallel and need different settings, such as a port per Spring Boot test class, can now bind a client to their thread, and keep plain `given()` in the test bodies:

```java
@BeforeEach
void useOwnPort() {
    RestAssured.useClient(RestAssuredClient.builder().port(port).build());
}


@AfterEach
void unbindClient() {
    RestAssured.resetClient();
}


@Test
void shows_open_orders() {
    given().queryParam("status", "open").when().get("/orders").then().statusCode(200);
}
```

`RestAssured.reset()` also unbinds the client, but it resets the static settings for every thread, so in a parallel suite use `resetClient()`. `given(client)` names a client for one request, for example for a second service. A client named in `given(client)` wins over the one bound to the thread, which wins over the static settings. The binding isn’t inherited by other threads, so a condition that Awaitility polls on its own thread uses the static settings unless it names the client with `given(client)` or Awaitility polls with `pollInSameThread()`.

A client is an immutable set of settings, built with `RestAssuredClient.builder()`, whose methods are named like the static fields and setters without `set`, or from the current static settings with `RestAssured.client()`. `toBuilder()` gives a builder for a changed copy. A client owns no connections or cookies and has no `close()`: its requests get their HTTP client by the same rules as those of the static API.

Without a client, everything works as before. See [Parallel tests](/docs/usage/parallel-tests/) and [RestAssuredClient](/docs/usage/parallel-tests/#restassuredclient) in the usage guide, and [Parallel tests with RestAssuredMockMvc](/docs/spring/mock-mvc/#parallel-tests-with-restassuredmockmvc) for Spring.

## Specifications no longer change when sent or validated

Sending a request, and validating a response with `then().spec(..)` or `validate(..)`, now works on copies of the request and response specification, so the specifications you hold never change. One specification can be sent, or validate responses, many times and from many threads, and sends the same request each time ([#945](https://github.com/rest-assured/rest-assured/issues/945)). Before, a specification sent twice sent the query parameters of the path twice, and one used from many threads mixed up the requests. The filter instances on a specification are still shared by every send, so a filter used from many threads must be thread safe.

Since the specification doesn’t change, you can no longer read back from it what the send set. `SpecificationQuerier.query(spec)` shows what was set before the send: no method, path or path parameters, no query parameters from the path’s query string, and no default headers, HTTP client, config or anything a filter added, such as a cookie from `CookieFilter` or the session id from `SessionFilter`. Filters and the failure listeners of `FailureConfig` get the copies, with everything set during the send. `given(requestSpecification, responseSpecification)` no longer links the two specifications, so afterwards, as for any built specification, `requestSpecification.get(..)` and `responseSpecification.when()` throw an `IllegalStateException`.

The config is fixed along the way:

* `given(requestSpecification, responseSpecification)` validates with the config of the request specification, then `RestAssured.config`, instead of `RestAssured.config` as it was when the response specification was built. A config set on `RestAssured.requestSpecification` applies to `expect()` assertions too.
* `then().spec(responseSpecification)` no longer leaves the config of the response in the specification, so a later `given(requestSpecification, responseSpecification)` doesn’t validate with it, and a failed validation logs its own request and response, not those of another thread.

## Where requests get their settings

In 7.0.0, a request gets the static settings, or those of its client, where it’s used, and not where a specification was built. This section shows what changes, each with an example.

### Spec builders no longer copy the static settings

`RequestSpecBuilder` and `ResponseSpecBuilder` no longer copy the static settings of `RestAssured` when they’re created, such as `baseURI`, `port`, `basePath`, `authentication`, `config`, `filters(..)`, `proxy`, `requestSpecification`, `rootPath`, the registered parsers and `defaultParser`. The static settings, or those of a client, are applied when the specification is used, with `given(..)`, `given().spec(..)`, `expect().spec(..)` or `then().spec(..)`, as they are at that time, and what was set on the builder is applied on top ([#1977](https://github.com/rest-assured/rest-assured/issues/1977)).

```java
RestAssured.requestSpecification = new RequestSpecBuilder().addHeader("X-Tenant", "a").build();
RequestSpecification spec = new RequestSpecBuilder().addQueryParam("q", "x").build();


given(spec).get("/search");
// Before: X-Tenant was sent twice, once from the copy in spec
// Now: X-Tenant is sent once
```

A change of `RestAssured.config`, `filters(..)`, `rootPath` or the registered parsers after the builder was created now reaches the request and its validation, and a specification used with a client gets the settings of the client. `SpecificationQuerier.query(spec)` shows only what was set on the builder:

```java
RestAssured.baseURI = "http://example.com";


SpecificationQuerier.query(new RequestSpecBuilder().build()).getBaseUri();
// Before: http://example.com
// Now: http://localhost
```

To keep a static setting with a specification, set it on the builder, for example `new RequestSpecBuilder().setConfig(RestAssured.config())` or `new ResponseSpecBuilder().rootPath(RestAssured.rootPath)`.

### What a builder computes while building

Some builder methods compute something right away, and they now use the config of the builder, the default one unless `setConfig(..)` sets another, instead of the static config: the body that `setBody(..)` serializes, the session id name of `setSessionId(value)`, the update strategy of `addParam(..)`, `addQueryParam(..)` and `addFormParam(..)`, the default control name and file name of `addMultiPart(..)`, and which headers `addHeader(..)` overwrites. They aren’t redone when the specification is used.

```java
RestAssured.config = RestAssured.config().sessionConfig(sessionConfig().sessionIdName("phpsessionid"));


new RequestSpecBuilder().setSessionId("1234").build();
// Before: sends the cookie phpsessionid=1234
// Now: sends JSESSIONID=1234


new RequestSpecBuilder().setConfig(RestAssured.config()).setSessionId("1234").build();
// Now: sends phpsessionid=1234
```

`log(..)` on a builder is the exception: it takes its settings from the config of the request when it logs, see [Logging](/release-notes/7-0/#logging).

### The Spring specification builders

`MockMvcRequestSpecBuilder` and `WebTestClientRequestSpecBuilder` no longer copy the static settings of `RestAssuredMockMvc` and `RestAssuredWebTestClient` either, such as the MockMvc or WebTestClient instance, `basePath`, `config`, `requestSpecification`, `responseSpecification`, `authentication`, `resultHandlers(..)` and `postProcessors(..)`. The same rules as for `RequestSpecBuilder` apply, with `setConfig(RestAssuredMockMvc.config())` or `setConfig(RestAssuredWebTestClient.config())` to compute with the static config.

```java
RestAssuredMockMvc.mockMvc(mockMvc);
RestAssuredMockMvc.resultHandlers(print());
MockMvcRequestSpecification spec = new MockMvcRequestSpecBuilder().addQueryParam("name", "Johan").build();


given().spec(spec).get("/greeting");
// Before: print() ran twice
// Now: print() runs once


spec.get("/greeting");
// Before: used the static MockMvc instance
// Now: fails with "You haven't configured a MockMVC instance"
```

A specification sent on its own needs its own instance: `new MockMvcRequestSpecBuilder().setMockMvc(mockMvc)`, or `setStandaloneSetup(..)` or `setWebAppContextSetup(..)`, and `setWebTestClient(..)` for WebTestClient. A MockMvc or WebTestClient instance, or a base path, set statically after the builder was created now reaches the request.

### given(requestSpecification, responseSpecification)

`given(requestSpecification, responseSpecification)` now merges both specifications into the static settings, or those of the client bound with `RestAssured.useClient(..)`, like `given().spec(requestSpecification).expect().spec(responseSpecification)`, instead of sending them as they were built. The static settings are read when `given(..)` is called, and `RestAssured.responseSpecification` now applies too.

```java
RequestSpecification request = new RequestSpecBuilder().addQueryParam("q", "x").build();
ResponseSpecification response = new ResponseSpecBuilder().expectContentType(ContentType.JSON).build();
RestAssured.port = 8081;
RestAssured.responseSpecification = new ResponseSpecBuilder().expectStatusCode(200).build();


given(request, response).get("/search");
// Before: sent to port 8080, the port when the builder was created, and the status code wasn't checked
// Now: sent to port 8081, and the status code must be 200
```

### Merging a response specification

Merging a response specification, with `expect().spec(..)`, `then().spec(..)`, `ResponseSpecBuilder.addResponseSpecification(..)`, `given(requestSpecification, responseSpecification)` or `RestAssured.responseSpecification`, now keeps the status code, status line, content type, response time, root path, default parser and log detail that the merged specification doesn’t set. Before, they were cleared.

```java
ResponseSpecification json = new ResponseSpecBuilder().expectContentType(ContentType.JSON).build();


expect().statusCode(200).spec(json).when().get("/orders");
// Before: the status code wasn't checked, since json expects none
// Now: the status code must be 200
```

Body expectations that the merged specification defined without a root path now take the root path in effect where they’re merged, such as `RestAssured.rootPath` or one set earlier in the chain:

```java
ResponseSpecification common = new ResponseSpecBuilder().expectBody("status", equalTo("ok")).build();


get("/orders/42").then().rootPath("data").spec(common);
// Before: checked status
// Now: checks data.status
```

To check `status` as before, merge `common` before you set the root path: `then().spec(common).rootPath("data")`.

### Equal headers and cookies are added once

Merging specifications, for example with `given().spec(..)`, `given(requestSpecification, responseSpecification)` or `RequestSpecBuilder.addRequestSpecification(..)`, no longer adds a header or cookie that’s already there with the same value, or the same multipart part, again. In the Spring modules, that includes the same result handler or post processor.

```java
RequestSpecification a = new RequestSpecBuilder().addHeader("X", "1").build();
RequestSpecification b = new RequestSpecBuilder().addHeader("X", "1").build();


given().spec(a).spec(b).get("/orders");
// Before: X: 1 was sent twice
// Now: X: 1 is sent once
```

So the static headers, cookies and multipart parts are also sent once when the merged specification was made with `given()` or `with()`, as in `given(with().header("X", "1"), expect().statusCode(200))`. A header with the same name and another value is still added, and a header that `HeaderConfig` overwrites, such as `Content-Type` or `Accept`, still replaces the current one.

### The page with the CSRF token

A relative path of the page with the CSRF token, set with `given().csrf(..)` or `CsrfConfig.csrfTokenPath(..)`, is now sent to the base URI, port and base path of the request, also with form authentication, instead of to `RestAssured.baseURI`, `port` and `basePath`.

```java
given().baseUri("http://localhost:8081").csrf("/users/form").formParam("firstName", "John").when().post("/users");
// Before: fetched the token from http://localhost:8080/users/form
// Now: fetches it from http://localhost:8081/users/form
```

For a request sent to a full URL, such as `post("http://localhost:8081/users")`, give the page with the CSRF token as a full URL too. The request for that page now also uses the config and the proxy of the request, so `relaxedHTTPSValidation()` or a proxy set on the request applies to it, and the extra requests of form authentication and CSRF use the client of the request.

### certificate(certURL, password)

`RestAssured.certificate(certURL, password)` now takes the SSL settings (the key store and trust store instances and types, the key store password, the host name verifier, the `SSLContext` and relaxed HTTPS validation) from the `SSLConfig` of the request when the request is sent: that of `given().config(..)` if it sets one, else that of the client of the request, else `RestAssured.config`.

```java
RestAssured.authentication = certificate("/client.p12", "secret");
RestAssured.config = RestAssured.config().sslConfig(sslConfig().keystoreType("PKCS12"));


get("https://localhost:8443/secured");
// Before: certificate(..) took the SSL settings of RestAssured.config when it was called, so the key store type was ignored
// Now: the key store type is used
```

The scheme is still a `CertAuthScheme`, but `getKeystoreType()`, `getTrustStoreType()`, `getKeyStore()`, `getTrustStore()`, `getHostnameVerifier()` and `getSslContext()` no longer show the settings that are used, and its setters don’t change what is sent. `given().auth().certificate(certURL, password)` and the `certificate(..)` methods that take a `CertificateAuthSettings` are unchanged.

### Logging

The logging filter of `log()`, as in `given().log().headers()`, and of `RequestSpecBuilder.log(..)`, `MockMvcRequestSpecBuilder.log(..)` and `WebTestClientRequestSpecBuilder.log(..)`, now takes the blacklisted headers, the print stream, the URL encoding of the request URI and pretty printing from the config of the request when it logs:

```java
given().
        log().headers().
        config(RestAssured.config().logConfig(logConfig().blacklistHeader("Authorization"))).
        header("Authorization", "Bearer secret").
when().
        get("/orders");
// Before: printed Authorization=Bearer secret, since log() read the config as it was when log() was called
// Now: prints Authorization=[ BLACKLISTED ]
```

Pretty printing given explicitly, as in `log().body(false)`, still wins.

`RequestLoggingFilter`, `ResponseLoggingFilter` and `ErrorLoggingFilter` created without a pretty-print argument now pretty print the body if the log config of the request has pretty printing enabled when they log:

```java
RestAssured.config = RestAssured.config().logConfig(logConfig().enablePrettyPrinting(false));


given().filter(new RequestLoggingFilter()).body("{\"a\":1}").post("/orders");
// Before: RequestLoggingFilter always pretty printed the body
// Now: prints {"a":1} as it is
```

Before, `ResponseLoggingFilter` and `ErrorLoggingFilter` took the setting of `RestAssured.config` when they were created. `expect().log().ifError()`, `ifStatusCodeIsEqualTo(..)` and `ifStatusCodeMatches(..)` follow the log config of the request too.

The response log now hides the headers blacklisted with `LogConfig.blacklistHeader(..)` or `blacklistDefaultSensitiveHeaders()` too. Before, only `then().log()` hid them, so `expect().log()`, `ResponseSpecBuilder.log(..)` and logging if validation fails printed their values, for example to the log of a CI build. These, the response logs of form authentication and CSRF, and a `ResponseLoggingFilter` or `ErrorLoggingFilter` created without blacklisted headers now take them from the log config of the request when they log:

```java
RestAssured.config = RestAssured.config().logConfig(logConfig().blacklistHeader("X-Session-Token"));
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();


get("/login").then().statusCode(200);
// Before: when the status code wasn't 200, the response log printed the value of X-Session-Token
// Now: it prints [ BLACKLISTED ] instead
```

Blacklisted headers that you give to the constructor of a logging filter are matched ignoring case, as in `LogConfig`, so they’re hidden also when the HTTP client gives the header names in lower case, as the JDK client does.

## JSON Schema validation and Jackson 3

`json-schema-validator` now uses the networknt json-schema-validator 3.0.9 (was 2.0.8). It’s built on Jackson 3 (`tools.jackson`), so the module brings Jackson 3, and no longer Jackson 2 or Guava. The java-json-tools json-schema-validator, which REST Assured used for draft-03 and draft-04, is now optional. Which validator validates a schema:

| Schema                                                                                   | Validated by                                                                                                                                                 |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Draft-06, draft-07, 2019-09 and 2020-12                                                  | The networknt json-schema-validator                                                                                                                          |
| Draft-04, and a schema without `$schema`, unless `schemaVersion(..)` picks another draft | The java-json-tools json-schema-validator if it’s on the classpath, exactly as in REST Assured 6. Otherwise the networknt json-schema-validator, as draft-04 |
| Draft-03                                                                                 | The java-json-tools json-schema-validator only                                                                                                               |

Add the java-json-tools json-schema-validator to your test dependencies to validate draft-03 schemas, to use a `JsonSchemaFactory` or checked validation, or to keep exactly the behavior of REST Assured 6 for draft-04 and schemas without `$schema`:

```xml
<dependency>
    <groupId>com.github.java-json-tools</groupId>
    <artifactId>json-schema-validator</artifactId>
    <version>2.2.14</version>
    <scope>test</scope>
</dependency>
```

Gradle:

```kotlin
testImplementation("com.github.java-json-tools:json-schema-validator:2.2.14")
```

Without it, a draft-03 schema fails with a `JsonSchemaValidationException` whose message says which dependency to add, and so does `settings().jsonSchemaFactory()`. Code that uses `JsonSchemaFactory` needs it to compile, and `checkedValidation(..)` has no effect without it. The two validators word their messages differently, so a test that checks the message of a failed validation may need a change.

REST Assured prefers Jackson 3 for object mapping when it’s on the classpath, for example in `as(Greeting.class)` and `body(greeting)`. So with `json-schema-validator` on the classpath, objects are now serialized and deserialized with Jackson 3, also when your project uses Jackson 2 and configures it with `jackson2ObjectMapperFactory(..)`. Jackson 3 still reads the `com.fasterxml.jackson.annotation` annotations, but not the modules and settings of a Jackson 2 `ObjectMapper`, and has other defaults. To keep Jackson 2:

```java
RestAssured.config = RestAssured.config().objectMapperConfig(objectMapperConfig().defaultObjectMapperType(ObjectMapperType.JACKSON_2));
```

The same goes for `RestAssuredMockMvc.config` and `RestAssuredWebTestClient.config`. For one request, use `as(Greeting.class, ObjectMapperType.JACKSON_2)` or `body(greeting, ObjectMapperType.JACKSON_2)`.

### JsonPath prefers Jackson 3

`JsonPath` (`json-path-gpath`) now prefers Jackson 3 over Jackson 2 when both are on the classpath and nothing else is configured, like REST Assured’s object mapping. So `response.jsonPath().getObject(..)` and `response.as(..)` deserialize with the same Jackson. Before, `getObject(..)` used Jackson 2 and `as(..)` Jackson 3. Without Jackson, `JsonPath` uses the first of Gson, Johnzon and JSON-B (Yasson) on the classpath, as before.

```java
Lotto lotto = get("/lotto").jsonPath().getObject("lotto", Lotto.class);
// Before: deserialized with Jackson 2 when both Jacksons are on the classpath
// Now: deserialized with Jackson 3, the same Jackson that as(..) uses
```

`response.jsonPath()` now also follows `ObjectMapperConfig.defaultObjectMapperType(..)` of the request, except `JAXB` and `JAKARTA_EE`, which only map XML, and uses the JSON-B factory of the `ObjectMapperConfig`, like its Jackson and Gson factories. Before, it ignored them. To keep Jackson 2:

```java
// For response.jsonPath(), and as(..) too
RestAssured.config = RestAssured.config().objectMapperConfig(objectMapperConfig().defaultObjectMapperType(ObjectMapperType.JACKSON_2));


// For the JsonPath instances that you create, such as JsonPath.from(json)
JsonPath.config = new JsonPathConfig().defaultParserType(JsonParserType.JACKSON_2);
```

## OAuth 1 without scribejava

`auth().oauth(..)` and `RestAssured.oauth(..)` now sign the request themselves, with HMAC-SHA1 as [RFC 5849](https://www.rfc-editor.org/rfc/rfc5849) describes, so OAuth 1 no longer needs scribejava. If you added `com.github.scribejava:scribejava-apis` to your build for REST Assured, remove it. A scribejava jar that’s still on the classpath isn’t used.

The `Authorization` header and the query string are written as with scribejava 2.5.3. Where scribejava didn’t follow RFC 5849, REST Assured now does, so a server that follows the RFC can verify these signatures:

* The parameters are sorted by their encoded names and values. scribejava sorted the decoded ones, which gives another order for names or values with characters other than letters, digits, `-`, `.`, `_` and `~`, such as non-ASCII names.
* A query parameter value that contains `=` is signed whole. scribejava signed only the part of the value before its first `=`.
* In the signed URI, the scheme and host are in lower case, an empty path is `/`, and user info and a fragment are left out.
* An empty consumer secret is allowed. scribejava failed with an `OAuthSignatureException`.

## Non-backward compatible changes

* REST Assured no longer includes a path engine or Groovy, and `json-path` and `xml-path` are renamed. See [Upgrading to 7.0](/release-notes/7-0/#upgrading-to-70).
* The `ObjectMapperConfig` factory getters and `JsonConfig.numberReturnType()` return the `rest-assured-common` types, and Jackson 1 is removed. See [Code that no longer compiles](/release-notes/7-0/#code-that-no-longer-compiles).
* REST Assured checks that `rest-assured-common`, `json-path-gpath` and `xml-path-gpath` have the same major and minor version as `rest-assured`.
* A JSON path that starts with `$.` or `$[`, as in Jayway JsonPath, now starts at the root, like `$` alone. For example, `$.store.book[0].title` is the same as `store.book[0].title`. Before, such a path gave `null` on an object. Keys that start with `$`, such as `$schema`, are unchanged, and `$..` is still not supported.
* When a `Map` or `Collection` request body is written as JSON as it is, which happens when the `ObjectMapper` given to `body(object, mapper)` returns one, the values that JSON has no type for, such as beans, records, `java.time` values, `Locale` and `File`, are now written by the configured object mapper, such as Jackson or Gson, instead of as an object of their bean properties. A `URI` is written as a string, like a `URL`, and an `Optional` as its value, or `null` when it’s empty. A `NaN` or infinite number fails with an `IllegalArgumentException`.
* A cookie without value, such as `given().cookie("a")`, is sent as `a=` instead of `a=null`.
* A `Cookie` header set with `header("Cookie", ..)` is no longer dropped when the request also has cookies. Both are sent in one `Cookie` header.
* Headers whose names differ only in case, such as `X-Multi` and `x-multi`, are both sent, under the name used first. Before, only the last one was sent.
* `auth().digest(..)` only answers a Digest challenge. Before, it also sent the password in clear text to a server that asked for Basic authentication. Use `basic(..)` to answer both.
* The `trustStore(path, password)` and `keyStore(path, password)` shortcuts no longer allow all host names. Add `allowAllHostnames()` to the SSL config to get the old behavior back.
* `multiPart("name", bytes)` with a `byte[]` sends the bytes as they are, as `application/octet-stream`, instead of as Base64 in a `text/plain` part.
* A `String` part without a charset is encoded in the charset of the multipart request (UTF-8 unless configured otherwise) instead of US-ASCII.
* Sending a request no longer changes the specification it was sent from, so you can’t read back from it what the send set. See [Specifications no longer change when sent or validated](/release-notes/7-0/#specifications-no-longer-change-when-sent-or-validated).
* The JDK’s HTTP client sends the requests instead of Apache HttpClient 4, which is no longer a dependency, nor are httpmime, httpcore, commons-codec and commons-logging. See [How the JDK client differs from HttpClient 4](/release-notes/7-0/#how-the-jdk-client-differs-from-httpclient-4).
* The APIs that take or return HttpClient 4 types, and `HTTPBuilder`, are removed. See [Removed APIs](/release-notes/7-0/#removed-apis).
* The SSL port settings, `sslConfig().port(..)` and `certAuthSettings().port(..)`, are removed. See [Removed APIs](/release-notes/7-0/#removed-apis).
* Timeouts, redirect errors, `HttpResponseException` and `ResponseParseException` are no longer HttpClient 4 exceptions. See [Exceptions](/release-notes/7-0/#exceptions).
* An HttpClient 4 parameter without a replacement fails the request. See [HttpClient 4 parameters](/release-notes/7-0/#httpclient-4-parameters).
* In OSGi, the `rest-assured` bundle requires the `http-client-jdk` bundle. See [OSGi and the HTTP client](/release-notes/7-0/#osgi-and-the-http-client).
* A request that sets a `Content-Length` or `Transfer-Encoding` header fails, whichever client sends it. See [Content-Length and Transfer-Encoding headers](/release-notes/7-0/#content-length-and-transfer-encoding-headers).
* `RequestSpecBuilder` and `ResponseSpecBuilder` no longer copy the static settings, and what a builder computes while building uses its own config. See [Spec builders no longer copy the static settings](/release-notes/7-0/#spec-builders-no-longer-copy-the-static-settings) and [What a builder computes while building](/release-notes/7-0/#what-a-builder-computes-while-building).
* `MockMvcRequestSpecBuilder` and `WebTestClientRequestSpecBuilder` no longer copy the static settings, so a specification sent on its own needs its own MockMvc or WebTestClient instance. See [The Spring specification builders](/release-notes/7-0/#the-spring-specification-builders).
* `given(requestSpecification, responseSpecification)` merges the specifications into the static settings, or those of the bound client, and applies `RestAssured.responseSpecification`. See [given(requestSpecification, responseSpecification)](/release-notes/7-0/#givenrequestspecification-responsespecification).
* Merging a response specification keeps the expectations that it doesn’t set, and its body expectations take the root path where they’re merged. See [Merging a response specification](/release-notes/7-0/#merging-a-response-specification).
* Merging specifications no longer adds a header or cookie that’s already there with the same value, or the same multipart part, result handler or post processor, again. See [Equal headers and cookies are added once](/release-notes/7-0/#equal-headers-and-cookies-are-added-once).
* A relative path of the page with the CSRF token is sent to the base URI, port and base path of the request. See [The page with the CSRF token](/release-notes/7-0/#the-page-with-the-csrf-token).
* `RestAssured.certificate(certURL, password)` takes the SSL settings of the request when it’s sent. See [certificate(certURL, password)](/release-notes/7-0/#certificatecerturl-password).
* The logging filters follow the log config of the request when they log. See [Logging](/release-notes/7-0/#logging).
* The root path methods deprecated since REST Assured 4.0.0, such as `root(..)`, `appendRoot(..)` and `JsonPath.setRoot(..)`, are removed. Use `rootPath(..)` and the other root path methods. See [Removed APIs](/release-notes/7-0/#removed-apis).
* `json-schema-validator` uses the networknt json-schema-validator 3.0.9, which brings Jackson 3 instead of Jackson 2 and Guava. REST Assured prefers Jackson 3 for object mapping when it’s on the classpath, so if you use Jackson 2, set `objectMapperConfig().defaultObjectMapperType(ObjectMapperType.JACKSON_2)`. See [JSON Schema validation and Jackson 3](/release-notes/7-0/#json-schema-validation-and-jackson-3).
* The java-json-tools json-schema-validator is optional in `json-schema-validator`. Add `com.github.java-json-tools:json-schema-validator:2.2.14` to validate draft-03 schemas, to use a `JsonSchemaFactory` or checked validation, or to keep exactly the behavior of REST Assured 6 for draft-04 and schemas without `$schema`. Without it, those are validated as draft-04 by the networknt json-schema-validator. See [JSON Schema validation and Jackson 3](/release-notes/7-0/#json-schema-validation-and-jackson-3).
* `JsonPath` prefers Jackson 3 over Jackson 2 when both are on the classpath, and `response.jsonPath()` follows `defaultObjectMapperType(..)` of the request. See [JsonPath prefers Jackson 3](/release-notes/7-0/#jsonpath-prefers-jackson-3).

## Other changes

* `ResponseSpecBuilder.setConfig(RestAssuredConfig)`, whose config the response specification validates with. A `LogConfig` with `enableLoggingOfRequestAndResponseIfValidationFails()` in it logs the request and the response when validation fails, or only the response with `then().spec(..)`, since the request was sent before.
* The OpenRewrite recipe `io.restassured.UpgradeToRestAssured7`, see [Let OpenRewrite do it](/release-notes/7-0/#let-openrewrite-do-it). It also replaces `ContextAuthenticationScheme`, which is deprecated and removed in REST Assured 8.0.0, with `AuthenticationScheme`, which takes the same lambda.
* `httpClientConfig().client(..)`, the system property `restassured.http.client` and `customizeClient(..)`, see [The HTTP client](/release-notes/7-0/#the-http-client).
* The `http-client-apache5` module, which sends the requests with Apache HttpClient 5, see [Apache HttpClient 5](/release-notes/7-0/#apache-httpclient-5). With it, `connectionConfig().closeIdleConnectionsAfterEachResponseAfter(..)` closes the idle connections of a client kept with `reuseHttpClientInstance()`, as in 6.x.
* Basic and Digest authentication, OAuth, redirects and the kept cookies of `keepResponseCookies(true)` are handled by REST Assured itself and work the same with every client. A `byte[]` body is sent again when it answers a challenge or follows a 307 or 308 redirect.
* Form authentication uses the first text input and the first password input of a login page that has several, and finds them whatever the case of their `type`.
* Deserializing an XML body to a generic type with the JAXB or Jakarta EE object mapper fails with an `IllegalArgumentException` that says JAXB can only deserialize to a class, instead of Groovy’s `MissingMethodException`.
* `RestAssuredClient`, `RestAssured.given(client)`, `useClient(..)`, `resetClient()` and `client()`, the same for `RestAssuredMockMvc` and `RestAssuredWebTestClient` with `RestAssuredMockMvcClient` and `RestAssuredWebTestClientClient`, and `Given(client)` in the Kotlin and Scala extensions. See [Clients for parallel tests](/release-notes/7-0/#clients-for-parallel-tests).
* Sending a `RestAssuredMockMvc` or `RestAssuredWebTestClient` specification no longer changes it. Each send gets its own logs, so with `enableLoggingOfRequestAndResponseIfValidationFails()` a specification sent several times, or from many threads, no longer logs the requests of the other sends.
* The request for the page with the CSRF token of `given().csrf(..)` and `CsrfConfig.csrfTokenPath(..)` uses the config and the proxy of the request, as with form authentication.
* `JsonPath.config`, `XmlPath.config` and `JsonSchemaValidator.settings` are standalone settings: they’re never taken from a request or a client, and `using(..)` sets them for one `JsonPath`, `XmlPath` or matcher.
* Hamcrest 3.0 (was 2.2), which has the same API. In OSGi, the `rest-assured` bundle accepts Hamcrest 2.2 and 3.x. Hamcrest 3.0’s own bundle imports `org.hamcrest` without a version, so if an older Hamcrest bundle is installed too, such as the 1.3 bundle that pax-exam’s JUnit 4 support uses, install Hamcrest 3.0 first, at a lower start level. Otherwise it binds to the older package and the `rest-assured` bundle doesn’t resolve.
* `json-path-jayway` depends on Jayway JsonPath 3.0.0 (was 2.10.0), which needs Java 17. It still works with Jayway JsonPath 2.8.0 and later, so a version that your build manages, such as Spring Boot 4’s 2.10.0, keeps working. In OSGi, Jayway JsonPath 3.0.0 requires the Jackson 3 bundles, see [OSGi](/release-notes/7-0/#osgi).
* `json-path-gpath`, `xml-path-gpath` and `rest-assured-all` depend on Groovy 5.1.3 (was 5.0.8) and work with Groovy 4.0, 5.x and 6.x. In OSGi, they import Groovy 4.0 up to, but not including, 7.0, instead of 5.x only. See [Groovy versions](/release-notes/7-0/#groovy-versions).
* `scala-extensions` depends on `scala3-library_3` 3.3.9 (was 3.3.8), the latest Scala 3.3 LTS. It keeps working with every newer Scala 3, and CI also tests it with the latest Scala 3 (3.10.0).
* The optional dependencies that REST Assured is compiled against, which you bring yourself, are upgraded: Jackson 2 (`jackson-databind`, `jackson-core` and `jackson-module-kotlin`) to 2.22.4, Jackson 3 (`tools.jackson`) to 3.2.4, Gson to 2.15.0, Jakarta XML Binding to 4 (`jakarta.xml.bind-api` 4.0.5 and `jaxb-runtime` 4.0.9, was 3.0.1 and 3.0.2), Spring Framework in `spring-mock-mvc`, `spring-web-test-client` and their Kotlin extensions to 7.0.10, Spring Security in `spring-mock-mvc` to 7.1.2 (was 6.5.11), and the Jakarta EE Web API in `spring-web-test-client-kotlin-extensions` to 11.0.0. In OSGi, the optional imports of `jakarta.xml.bind` accept Jakarta XML Binding 3.x and 4.x (was 3.x only), and the optional import of `com.google.gson` accepts Gson 2.15 or later (was 2.14 or later).
* The OSGi bundles now require a Java 17 execution environment (`osgi.ee` JavaSE 17), which every Java 17 framework provides. See [OSGi](/release-notes/7-0/#osgi).
* `spring-web-test-client` no longer depends on `org.synchronoss.cloud:nio-multipart-parser`, which Spring Framework 6 and later don’t use, so it, `nio-stream-storage` and `slf4j-api` 1.7.12 leave your classpath.
* OAuth 1 (`auth().oauth(..)`) no longer needs scribejava, and in OSGi the `rest-assured` bundle no longer imports `com.github.scribejava`. Signatures now follow RFC 5849 where scribejava didn’t. See [OAuth 1 without scribejava](/release-notes/7-0/#oauth-1-without-scribejava).
* A config set on a request specification, such as `new RequestSpecBuilder().setConfig(..)`, now also applies to `expect()` assertions, and `given(requestSpecification, responseSpecification)` with logging if validation fails logs the request too, not only the response.
* Every response log hides the blacklisted headers, not only `then().log()`. See [Logging](/release-notes/7-0/#logging). `LogConfig.blacklistDefaultSensitiveHeaders()` no longer adds `Authorization`, `Proxy-Authorization` and `Cookie` to the blacklist of the `LogConfig` it’s called on, only to the new `LogConfig` that it returns.
* Fixes for `auth().oauth2(accessToken, OAuthSignature.QUERY_STRING)`, `closeIdleConnectionsAfterEachResponse()` with large bodies, `dontCloseIdleConnectionsAfterEachResponse()`, `reuseHttpClientInstance()` after a response whose body was never read, `RestAssured.keyStore(password)`, `ProxySpecification.withScheme(..)`, multipart requests in `RestAssuredWebTestClient`, and pretty printing of a JSON string with an escaped backslash followed by `u`.

See the [change log](https://raw.githubusercontent.com/rest-assured/rest-assured/master/changelog.txt) for all changes.