This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch docs
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/docs by this push:
     new 5e231e822e feat: declarative next-gen @Remote/@RemoteOp features 
(constant parts, call policy, @Url/baseUrl, @Multipart, format selection, 
streaming)
5e231e822e is described below

commit 5e231e822e4c6c9786c4f6b299875a749ebbf0cf
Author: James Bognar <[email protected]>
AuthorDate: Fri Jun 26 08:50:08 2026 -0400

    feat: declarative next-gen @Remote/@RemoteOp features (constant parts, call 
policy, @Url/baseUrl, @Multipart, format selection, streaming)
    
    Next-gen REST client (RestClient.remote) only; classic engine unchanged.
    - Constant interface/method headers, queryData, formData; @Content(def) 
defaults.
    - Per-part serializer via @HttpPartMarshalling.
    - Annotation call policy: interceptors, timeout, safe idempotent-only 
retries, throwOnError.
    - Dynamic @Url parameter + declarative baseUrl with http/https SSRF guard.
    - Declarative @Multipart/@Part with streaming part sources.
    - Per-method accept/contentType marshaller selection.
    - Streaming request/response bodies (SerializerBody, ReaderBody) + response 
stream lifecycle fix.
    - fix: explicit jakarta.servlet.http.Part import in RequestFormParamList to 
resolve @Part name collision.
    - test: context-scoped ReadinessState in HealthServlet_Test for reused-JVM 
isolation.
    - docs: NextGenRestClient/Interceptors/RequestParts pages + 10.0.0 release 
notes.
---
 pages/release-notes/10.0.0.md           |  17 ++
 pages/topics/13.03.RequestParts.md      |  29 ++-
 pages/topics/13.09.Interceptors.md      |  30 ++-
 pages/topics/13.15.NextGenRestClient.md | 317 ++++++++++++++++++++++++++++++++
 4 files changed, 391 insertions(+), 2 deletions(-)

diff --git a/pages/release-notes/10.0.0.md b/pages/release-notes/10.0.0.md
index dfd4955293..f08d5facd9 100644
--- a/pages/release-notes/10.0.0.md
+++ b/pages/release-notes/10.0.0.md
@@ -318,6 +318,23 @@ Juneau 10.0 extends its request-boundary observability to 
cover custom (non-requ
 
 See the extended [Observability — Micrometer + 
OpenTelemetry](/docs/topics/RestServerObservability) topic page.
 
+### juneau-rest-client / juneau-rest-common
+
+#### Next-generation remote-proxy declarative features
+
+Juneau 10.0 significantly expands what the [REST 
proxy](/docs/topics/RestProxyBasics) annotation family (`@Remote`, `@RemoteOp`, 
and the verb annotations `@RemoteGet` / `@RemotePost` / `@RemotePut` / 
`@RemoteDelete` / `@RemotePatch`) can express declaratively. Every addition 
below is honored by the **next-generation** proxy engine only 
(`RestClient.remote(...)` → `RemoteClient`); the classic `getRemote(...)` 
engine is unchanged and ignores these members.
+
+- **Constant part values** — `@Remote` (interface) and `@RemoteOp` / verb 
annotations (method) gain `headers` / `queryData` / `formData` members that 
emit always-applied constant headers, query parameters, and form-data fields on 
every call, with no dummy parameter. Method-level constants take precedence 
over interface-level ones, caller-supplied values still compose, and all values 
resolve through `VarResolver.DEFAULT`. (Constant path values are out of scope; 
`@Remote(headerList=…)` rem [...]
+- **`@Content(def=…)` honored** — the next-gen engine now applies both the 
parameter-level body default (when the body argument is `null`) and a 
param-less, method-level constant body, matching the classic engine.
+- **Per-part serializers** — `@HttpPartMarshalling(serializer=…)` is now wired 
for outgoing query / header / path / form-data parts, with precedence parameter 
› method › interface and a fallback to the default `OpenApiSerializer` when 
absent. (Serializer side only; the parser member is not consumed by the 
next-gen engine.)
+- **Declarative call policy** — `@Remote` and `@RemoteOp` / verb annotations 
gain `interceptors()`, `timeout()`, `retries()`, `retryNonIdempotent()`, and 
`throwOnError()`. Interceptors apply as a union (builder → interface → method); 
timeout/retries/throwOnError scalars follow method › interface › 
builder-default precedence. Auto-retries are conservatively safe: triggered 
only by connection failures or `429`/`5xx`, with exponential backoff, and gated 
to idempotent verbs (POST/PATCH requi [...]
+- **Dynamic URL & base override** — a new `@Url` parameter annotation 
(`org.apache.juneau.http.Url`) supplies the whole request URL at call time 
(absolute replaces and bypasses the client root URL; relative resolves against 
it), plus a declarative `baseUrl()` attribute on `@Remote` / `@RemoteOp` / verb 
annotations that substitutes the authority+root while preserving the path and 
`{var}` templating. Precedence: `@Url` › method `baseUrl` › interface `baseUrl` 
› client `rootUrl`. URL/base o [...]
+- **Declarative multipart** — a new method-level `@Multipart` marker 
(`org.apache.juneau.http.remote.Multipart`) plus a `@Part` parameter annotation 
(`org.apache.juneau.http.Part`, with `name`/`value`/`fileName`/`contentType`) 
build a `multipart/form-data` request from annotated parameters. Text, 
`byte[]`, `File`, `InputStream`, `Reader`, `HttpBody`, and bean part sources 
are accepted; file/stream/reader/bean parts stream rather than buffer. A method 
is either multipart or single-`@Conte [...]
+- **Per-method format selection** — `contentType()` and `accept()` attributes 
on `@Remote` / `@RemoteOp` / verb annotations drive marshaller **selection** 
(not just header values). `contentType` selects the matching request serializer 
and emits a single clean `Content-Type`; `accept` sets the `Accept` header and 
acts as a fallback parser (the response `Content-Type` stays authoritative). A 
no-match media type falls back to the default marshaller but still sends the 
overridden label (vend [...]
+- **Streaming efficiency** — POJO and `Reader` request bodies now stream 
straight to the wire instead of being buffered, and `Reader` / `InputStream` 
return types hand the caller a lazy stream over the live response whose 
connection is released on close.
+
+See the expanded [Declarative Remote-Proxy 
Features](/docs/topics/NextGenRestClient#declarative-remote-proxy-features-next-gen)
 section of the Next-Generation REST Client topic page for full examples and 
precedence rules.
+
 ### juneau-marshall
 
 #### Token-Streaming and Record-Streaming API
diff --git a/pages/topics/13.03.RequestParts.md 
b/pages/topics/13.03.RequestParts.md
index 9a822dfb56..e20d2b9a56 100644
--- a/pages/topics/13.03.RequestParts.md
+++ b/pages/topics/13.03.RequestParts.md
@@ -51,4 +51,31 @@ RestClient client = 
RestClient.create().header("Authorization", ()->getMyAuthTok
 
 :::info See Also
 - <a href="/site/apidocs/org/apache/juneau/http/header/package-summary.html" 
target="_blank">org.apache.juneau.http.header</a> - Predefined <a 
href="https://hc.apache.org/httpcomponents-core-4.4.x/current/httpcore/apidocs/org/apache/http/Header.html";
 target="_blank">Header</a> beans.
-:::
\ No newline at end of file
+:::
+
+## Constant Parts on Remote-Proxy Interfaces
+
+The above methods add parts on a per-client or per-request basis. When using 
the next-generation
+[remote-proxy](/docs/topics/RestProxyBasics) engine 
(`RestClient.remote(...)`), you can also declare
+**always-applied constant** headers, query parameters, and form-data fields 
directly on the proxy
+annotations — with no corresponding method parameter:
+
+- Interface-level: `@Remote(headers=…, queryData=…, formData=…)` — applied to 
every method.
+- Method-level: `@RemoteOp(headers=…, queryData=…, formData=…)` (and the verb 
annotations) — applied
+  to that method, taking precedence over an interface-level constant of the 
same name.
+
+:::tip Example
+```java
+@Remote(path="/api", queryData="api_key=$S{petstore.apiKey}")
+public interface PetStore {
+
+    @RemoteGet(path="/pets", headers="X-View: full", queryData="view=full")
+    Pet[] getPets();
+}
+```
+:::
+
+These constants are honored by the next-gen engine only; the classic 
`getRemote(...)` engine ignores
+them. See
+[Declarative Remote-Proxy 
Features](/docs/topics/NextGenRestClient#constant-part-values) for the full
+contract (precedence, `VarResolver` support, and composition with 
caller-supplied values).
\ No newline at end of file
diff --git a/pages/topics/13.09.Interceptors.md 
b/pages/topics/13.09.Interceptors.md
index 83bda93d3e..26ecbd4c3e 100644
--- a/pages/topics/13.09.Interceptors.md
+++ b/pages/topics/13.09.Interceptors.md
@@ -44,4 +44,32 @@ RestClient client = RestClient
     )
     .build();
 ```
-:::
\ No newline at end of file
+:::
+
+## Annotation-Level Interceptors (Next-Gen Remote Proxies)
+
+When using the next-generation remote-proxy engine (`RestClient.remote(...)`), 
interceptors can also
+be declared **declaratively** on the proxy annotations rather than only on the 
builder. The
+`interceptors()` member is available on `@Remote` (interface-level) and on 
`@RemoteOp` /
+`@RemoteGet` / `@RemotePost` / `@RemotePut` / `@RemoteDelete` / `@RemotePatch` 
(method-level). Each
+class must implement `RestCallInterceptor` and provide a public no-arg 
constructor.
+
+:::tip Example
+```java
+@Remote(path="/api", interceptors=AuthInterceptor.class)   // applied to every 
method
+public interface MyApi {
+
+    @RemoteGet(path="/audited", interceptors=AuditInterceptor.class)  // plus 
this method only
+    Data getData();
+}
+```
+:::
+
+The builder-configured, interface-level, and method-level interceptors are 
applied as a **union** in
+the order **builder → interface → method** (the method-level interceptor runs 
closest to the call).
+
+The same annotations also carry the rest of the declarative call policy — 
per-call `timeout`, safe
+`retries` / `retryNonIdempotent`, and `throwOnError`. These are honored by the 
next-gen engine only;
+the classic `getRemote(...)` engine ignores them. See
+[Declarative Remote-Proxy 
Features](/docs/topics/NextGenRestClient#call-policy-interceptors-timeouts-retries-throwonerror)
+for the full call-policy contract.
\ No newline at end of file
diff --git a/pages/topics/13.15.NextGenRestClient.md 
b/pages/topics/13.15.NextGenRestClient.md
index 96f402ce84..3029acc248 100644
--- a/pages/topics/13.15.NextGenRestClient.md
+++ b/pages/topics/13.15.NextGenRestClient.md
@@ -147,6 +147,323 @@ client.shutdown();
 
 ---
 
+## Declarative Remote-Proxy Features (Next-Gen)
+
+The next-generation engine adds a family of declarative capabilities to the
+[REST proxy](/docs/topics/RestProxyBasics) annotations (`@Remote`, 
`@RemoteOp`, and the verb
+annotations `@RemoteGet` / `@RemotePost` / `@RemotePut` / `@RemoteDelete` / 
`@RemotePatch`).
+
+:::warning Next-gen engine only
+Every feature in this section is honored **only** by the next-generation proxy 
engine obtained via
+`RestClient.remote(MyProxy.class)` → `RemoteClient`. The classic engine 
obtained via
+`RestClient.getRemote(...)` is unchanged and ignores these annotation members. 
Where an attribute is
+visible on a shared annotation, its Javadoc states that the classic engine 
currently ignores it.
+:::
+
+```java
+// Next-gen proxy — honors everything in this section.
+MyProxy proxy = client.remote(MyProxy.class);
+
+// Classic proxy — unchanged; ignores the next-gen-only members below.
+MyProxy classic = client.getRemote(MyProxy.class, "https://api.example.com";);
+```
+
+### Constant Part Values
+
+`@Remote` (interface) and `@RemoteOp` / the verb annotations (method) can 
declare **always-applied
+constant** headers, query parameters, and form-data fields that are emitted on 
every call — with no
+corresponding method parameter and no dummy `null` argument.
+
+- Interface-level: `@Remote(headers=…, queryData=…, formData=…)`.
+- Method-level: `@RemoteOp(headers=…, queryData=…, formData=…)` (and the verb 
annotations).
+- `headers` use the `"Name: value"` form; `queryData` / `formData` use the 
`"name=value"` form.
+- All values resolve through `VarResolver.DEFAULT` (e.g. 
`"$S{mySystemProperty}"`).
+- A method-level constant of the same name **takes precedence over** an 
interface-level constant.
+- A caller-supplied parameter value of the same name still **composes** with 
the constant (the
+  constant is not suppressed). This is distinct from parameter-level
+  [`def` defaults](/docs/topics/RestProxyBasics#default-values), which only 
fill a `null` argument.
+
+:::note Constant path values are out of scope
+There is no constant path attribute at either level. Path segments remain 
driven by the `path`
+template's `{var}` tokens and `@Path` parameters.
+:::
+
+```java
+@Remote(
+    path="/api",
+    queryData="api_key=$S{petstore.apiKey}"   // every call carries ?api_key=…
+)
+public interface PetStore {
+
+    @RemoteGet(
+        path="/pets",
+        headers="X-View: full",               // every getPets() call sends 
this header
+        queryData="view=full"                  // …and ?view=full
+    )
+    Pet[] getPets();
+}
+```
+
+:::note `@Remote(headerList=…)` is classic-only
+Per design decision D1, the `@Remote(headerList=…)` member (a supplier of an 
Apache-HttpClient
+`HeaderList`) remains **classic-only**. The next-gen engine does not consume 
it — use the
+transport-agnostic `headers="Name: value"` form instead.
+:::
+
+### Content Body Defaults
+
+The next-gen engine honors `@Content(def=…)` — both the parameter-level 
default (used when a body
+argument is `null`) and the param-less, method-level constant body (used when 
a method has no body
+parameter at all). This matches the classic engine's behavior and the 
documentation under
+[Default Values](/docs/topics/RestProxyBasics#default-values). Provided values 
still win.
+
+```java
+@Remote(path="/petstore")
+public interface PetStore {
+
+    // Parameter-level default — fills a null argument.
+    @RemotePost("/pets")
+    Pet addPet(@Content(def="{name:'Unknown',price:0}") CreatePet pet);
+
+    // Param-less constant body — sent on every call.
+    @RemotePost("/pets/ping")
+    @Content(def="{}")
+    void ping();
+}
+```
+
+### Per-Part Serializers
+
+A specific request **part** (query / header / path / form-data) can be 
marshalled with a custom
+`HttpPartSerializer` via `@HttpPartMarshalling(serializer=…)`, instead of the 
engine's default
+`OpenApiSerializer`. The annotation can sit on a parameter, on the method, or 
on the interface, with
+precedence **parameter › method › interface**; when absent, the existing 
default serializer is used
+unchanged.
+
+```java
+@Remote(path="/api")
+public interface MyApi {
+
+    @RemoteGet("/search")
+    String search(
+        @Query("filter")
+        @HttpPartMarshalling(serializer=MyFilterPartSerializer.class)  // this 
part only
+        Filter filter
+    );
+}
+```
+
+:::note Serializer side only
+Only the serializer (request) side is wired in the next-gen engine; the 
`parser` member is not
+consumed because the next-gen engine has no response-part parse path. This 
applies to HTTP **parts**
+only — request *bodies* are handled by [per-method format 
selection](#per-method-format-selection)
+and full serializers, not `HttpPartSerializer`.
+:::
+
+### Call Policy: Interceptors, Timeouts, Retries, throwOnError
+
+`@Remote` and `@RemoteOp` / the verb annotations can declare cross-cutting 
call behavior
+declaratively, instead of configuring it only on the `RestClient` builder. See 
also the
+[Interceptors](/docs/topics/Interceptors) page.
+
+| Member | Type | Behavior |
+|---|---|---|
+| `interceptors()` | `Class<?>[]` | Classes implementing the client 
`RestCallInterceptor` SPI (public no-arg constructor). |
+| `timeout()` | `String` | Per-call response/read timeout as a duration string 
(e.g. `"30s"`, `"1500ms"`). |
+| `retries()` | `int` | Maximum automatic retry attempts (`0` = disabled). |
+| `retryNonIdempotent()` | `boolean` | Opt `POST`/`PATCH` into auto-retry. |
+| `throwOnError()` | `boolean` | Throw a generic exception on an unmatched 
error response. |
+
+```java
+@Remote(path="/api", interceptors=AuthInterceptor.class, timeout="30s")
+public interface MyApi {
+
+    @RemoteGet(path="/flaky", retries=3, throwOnError=true)
+    Data getData();
+
+    @RemotePost(path="/orders", retries=2, retryNonIdempotent=true)
+    Order createOrder(@Content Order order);
+}
+```
+
+**Interceptors** are applied as a **union** in the order **builder → interface 
→ method** (the
+method-level interceptor runs closest to the call).
+
+**Timeout / retries / throwOnError** scalars follow **method › interface › 
builder default**
+precedence (`throwOnError` and `retryNonIdempotent` are the logical-OR of the 
method and interface
+values). The per-call timeout is the response/read timeout; the connect 
timeout remains a
+client-level setting.
+
+**Safe retries** are deliberately conservative (hard gates, all must pass):
+
+- **Trigger** — a connection failure, or a retryable HTTP status (`429` or any 
`5xx`), with a short
+  exponential backoff between attempts.
+- **Idempotent verbs only** — `GET`/`PUT`/`DELETE`/`HEAD` auto-retry; 
`POST`/`PATCH` retry only when
+  `retryNonIdempotent=true`.
+- **Repeatable bodies only** — a request whose body is not repeatable (e.g. a 
streaming
+  `InputStream` / `Reader` body) is never resent. A serialized-POJO body 
**is** repeatable.
+- **Buffered return modes only** — retries are disabled for `RESPONSE`, raw 
`InputStream` / `Reader`
+  returns, streaming cursors, and `Future` / `CompletableFuture` returns.
+
+**`throwOnError`** composes *after* the typed-exception mapping from the 
method's `throws` clause: a
+declared exception type whose status matches the response is still thrown in 
preference; only when no
+declared type matches and the status is `>=400` does the generic exception 
fire.
+
+### Dynamic URLs and Base Overrides
+
+Two complementary ways to point a call at a runtime- or annotation-chosen 
endpoint:
+
+- **`@Url` parameter** (`org.apache.juneau.http.Url`) — the Retrofit `@Url` 
pattern. The argument's
+  value becomes the effective URL for that single call. A value containing a 
scheme (`://`) is
+  **absolute** — it replaces the whole endpoint and bypasses the client root 
URL; a value with no
+  scheme is **relative** — it resolves against the client root URL only (not 
against the interface
+  `@Remote(path)`). `{var}` tokens are still filled by `@Path` parameters. At 
most one `@Url` per
+  method; a `null`/blank value is rejected.
+- **`baseUrl()` attribute** on `@Remote` / `@RemoteOp` / the verb annotations 
— substitutes only the
+  authority+root of the URL while **preserving** the interface base path, the 
method path, and
+  `{var}` templating. Resolves through `VarResolver.DEFAULT`.
+
+Precedence (most-specific first): **`@Url` parameter › method `baseUrl` › 
interface `baseUrl` →
+client `rootUrl`**.
+
+```java
+@Remote(path="/api", baseUrl="$S{myservice.baseUrl}")
+public interface MyApi {
+
+    // Caller chooses the endpoint at runtime.
+    @RemoteGet
+    String fetch(@Url String url);
+
+    // Overrides the host for this one method only.
+    @RemoteGet(path="/health", baseUrl="http://health-host:8080";)
+    String health();
+}
+```
+
+:::warning SSRF guardrail
+Only `http` / `https` schemes are permitted on a URL/base override. Other 
schemes (`file:`,
+`gopher:`, `jar:`, …) are rejected with a clear error.
+:::
+
+### Declarative Multipart
+
+Beyond the [fluent multipart API](#multipart-file-uploads), the next-gen 
engine supports a
+Retrofit-style **declarative** `multipart/form-data` surface:
+
+- `@Multipart` (`org.apache.juneau.http.remote.Multipart`) — a method-level 
marker. The
+  `multipart/form-data` `Content-Type` (with a generated boundary) is applied 
automatically.
+- `@Part` (`org.apache.juneau.http.Part`) — a parameter annotation with 
`name()` / `value()`
+  (synonym), `fileName()`, and `contentType()` members.
+
+```java
+@Remote(path="/api")
+public interface UploadService {
+
+    @RemotePost("/upload")
+    @Multipart
+    String upload(
+        @Part("title") String title,
+        @Part(name="attachment", fileName="report.pdf", 
contentType="application/pdf") byte[] data
+    );
+}
+```
+
+Accepted part-source types (mapped automatically from the argument type):
+
+| Argument type | Part behavior |
+|---|---|
+| `String` / `CharSequence` / scalars | Text field |
+| `byte[]` | Binary, sent verbatim (repeatable) |
+| `File` | **Streamed**; `fileName` defaults to the file's name |
+| `InputStream` | **Streamed** (one-shot; makes the body non-repeatable) |
+| `Reader` | **Streamed** as UTF-8 (one-shot) |
+| `HttpBody` | Used directly |
+| any other object (bean) | Serialized with the client's default serializer |
+
+A `null` `@Part` argument contributes no part. File / stream / reader / bean 
parts **stream** (they
+reuse the streaming bodies described under [Streaming 
Bodies](#streaming-request-and-response-bodies)).
+
+**Body-mode exclusivity** is validated at proxy-build time 
(`IllegalArgumentException`): a method is
+either multipart (`@Multipart` + one or more `@Part`) **or** single-body 
(`@Content`), never both;
+`@Multipart` with no `@Part`, or `@Part` without `@Multipart`, is also 
rejected.
+
+:::tip Escape hatch (Option B)
+For advanced cases you can still hand-assemble a `MultipartBody` and pass it 
as a single `@Content`
+body without `@Multipart` — that path is unchanged.
+:::
+
+### Per-Method Format Selection
+
+`@Remote` (interface default) and `@RemoteOp` / the verb annotations (method) 
expose `contentType()`
+and `accept()` media-type attributes that drive **marshaller selection** — not 
merely header values.
+This lets a single method switch its actual wire format without changing the 
client default for every
+other call (e.g. one legacy XML endpoint on an otherwise-JSON API, or a binary 
format on a few
+endpoints). See also [Content-Type 
Negotiation](/docs/topics/ContentTypeNegotiation).
+
+- **`contentType`** selects the matching registered **request serializer** 
from the client's
+  serializer set (so the body bytes are actually written in that format) and 
sets a single, clean
+  `Content-Type` header — replacing the serializer's default content type with 
no duplicate header.
+- **`accept`** sets the `Accept` header and acts as a **fallback parser** 
only. The **response**
+  `Content-Type` stays authoritative for parser selection; the `accept` media 
type is used only when
+  the response is unlabeled or its `Content-Type` matches no registered parser.
+- **No-match media type** falls back to the default marshaller but still sends 
the overridden label
+  (supporting vendor types such as `application/vnd.example.v2+json`).
+
+Precedence: **method › interface › client default**. A dedicated 
`contentType`/`accept` attribute
+beats a [constant](#constant-part-values) `Content-Type`/`Accept` header (the 
constant is dropped, no
+duplicate), but a genuinely caller-supplied `@Header("Content-Type"/"Accept")` 
parameter still wins.
+
+```java
+@Remote(path="/api")            // interface defaults to the client's JSON 
marshaller
+public interface MyApi {
+
+    @RemoteGet("/items")
+    Item[] getItems();          // JSON request/response
+
+    // This one legacy endpoint speaks XML in both directions.
+    @RemotePost(path="/legacy", contentType="application/xml", 
accept="application/xml")
+    Result postLegacy(@Content Payload payload);
+}
+```
+
+### Streaming Request and Response Bodies
+
+The next-gen engine streams large bodies in both directions instead of 
buffering them in memory,
+matching the classic engine's efficiency. See also [Streaming 
Cursors](/docs/topics/StreamingCursors).
+
+- **Streaming POJO request bodies** — a POJO `@Content` body is serialized 
straight to the wire
+  (chunked, `Content-Length: -1`) rather than pre-serialized to a 
`String`/`byte[]`. This body is
+  **repeatable**, so it is safe to auto-retry.
+- **Streaming `Reader` request bodies** — a `Reader` `@Content` body streams 
as UTF-8 rather than
+  being drained to a `String` first. (Non-repeatable — never auto-retried.)
+- **`Reader` and `InputStream` return types** — a method may declare a 
`Reader` or `InputStream`
+  return type and receive a lazy stream over the live response. The caller 
**owns** the stream; the
+  underlying connection is released when the caller closes it. Always close 
the returned stream
+  (try-with-resources).
+
+```java
+@Remote(path="/api")
+public interface MyApi {
+
+    // Streams the POJO body straight to the socket.
+    @RemotePost("/bulk")
+    void bulkUpload(@Content List<Record> records);
+
+    // Caller owns the returned stream — close it to release the connection.
+    @RemoteGet("/export")
+    InputStream export();
+}
+```
+
+```java
+MyApi api = client.remote(MyApi.class);
+try (InputStream in = api.export()) {
+    in.transferTo(System.out);
+}
+```
+
+---
+
 ## Multipart File Uploads
 
 `multipart/form-data` is a first-class body type:

Reply via email to