Skip to content

[bugfix] http-client: support the src attribute on http:body (#6510)#6511

Merged
line-o merged 2 commits into
eXist-db:developfrom
joewiz:bugfix/http-client-body-src
Jul 5, 2026
Merged

[bugfix] http-client: support the src attribute on http:body (#6510)#6511
line-o merged 2 commits into
eXist-db:developfrom
joewiz:bugfix/http-client-body-src

Conversation

@joewiz

@joewiz joewiz commented Jun 22, 2026

Copy link
Copy Markdown
Member

[This PR was prompted by Joe, drafted by Claude Code, and reviewed by Joe.]

Summary

Implements the src attribute on http:body in the native EXPath HTTP Client, fixing #6510. Previously @src was parsed away and an empty body was sent with no error — so e.g. a PUT with <http:body media-type='text/plain' src='/db/tmp/test.txt'/> stored an empty resource.

Closes #6510

What changed

  • RequestBuilderhttp:body parsing now reads @src. When present, the body is the linked resource rather than the element's content. resolveBodySource(XQueryContext) resolves @src to the request-body bytes, and applyBody sends them with @media-type as the Content-Type.
  • SendRequestFunction — calls resolveBodySource(context) between parse and build (resolution needs the broker from the query context), so the built HttpRequest carries the resolved bytes.

Spec conformance (EXPath HTTP Client 3.1)

The src attribute can be used in a request to set the body content as the content of the linked resource… When this attribute is used, only the media-type attribute must also be present, and there can be neither content in the http:body element, nor any other attribute, or this is an error [err:HC004].

  • @src is resolved like fn:doc, via SourceFactory: a database path (/db/… or xmldb:), a file: URI, or an http:/classpath location all work; the resource bytes are sent verbatim. This matches the EXPath reference implementation (BaseX), which writes the src resource's raw bytes.
  • err:HC004 (the code was already defined in HttpClientModule but never thrown) is raised for the spec's conflict conditions: @src combined with body content, or @src with any attribute other than media-type.
  • media-type is mandatory on the body, raised as err:HC005 (a request-validity error — HC004 is reserved for the src/content conflict, matching eXist's own HC004 description and BaseX's "media-type is mandatory" behavior).

Test plan

  • SendRequestFunctionTest.bodySrcSendsResourceContent — stores a db resource, sends it via @src to an echo endpoint, and asserts the body was transmitted (uses the existing embedded HttpServer harness; no external service).
  • SendRequestFunctionTest.bodySrcWithContentThrowsHC004@src plus inline content raises err:HC004.
  • SendRequestFunctionTest.bodySrcWithoutMediaTypeThrowsHC005@src without media-type raises err:HC005.
  • Full SendRequestFunctionTest green (76/76).
  • Codacy/PMD clean on the changed files.

Note

This touches RequestBuilder, which #6503 (the HttpClient caching/JMX refactor) is also revising. The two are independent in intent — #6503 is a refactor, this is a missing-feature fix — but whichever merges second will need a small rebase in RequestBuilder.

@dizzzz

dizzzz commented Jun 22, 2026

Copy link
Copy Markdown
Member

I'd like to have #6503 pulled in first...

Comment on lines +763 to +767
"let $response := http:send-request(\n" +
" <http:request method='POST' href='" + baseUrl() + "/echo'>\n" +
" <http:body media-type='text/plain' src='/db/http-src-body.txt'/>\n" +
" </http:request>)\n" +
"return parse-json($response[2])?body");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use text block in combination with formatted for better readability

Comment on lines +817 to +821
HTTP_NS +
"http:send-request(\n" +
" <http:request method='POST' href='" + baseUrl() + "/echo'>\n" +
" <http:body src='/db/http-src-body.txt'/>\n" +
" </http:request>)");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use text block in combination with formatted for better readability

joewiz added a commit to joewiz/exist that referenced this pull request Jun 24, 2026
Per @reinhapa's review of PR eXist-db#6511:

- Replace the two try/catch + fail() exception tests (HC004, HC005) with
  assertj assertThatExceptionOfType(...).isThrownBy(...).withStackTraceContaining(...).
  withStackTraceContaining preserves the original message-plus-cause matching
  (the error code can surface in the cause chain). Declare assertj-core as a
  test dependency in the module pom (version is managed in exist-parent).
- Convert the three new query strings to text blocks with formatted(baseUrl()).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@joewiz

joewiz commented Jun 24, 2026

Copy link
Copy Markdown
Member Author

[This response was prompted by Joe, drafted by Claude Code, and reviewed by Joe.]

Thanks @reinhapa. Addressed in 79b9cb9:

  • The two exception tests (HC004, HC005) now use assertThatExceptionOfType(...).isThrownBy(...).withStackTraceContaining(...) instead of try/catch + fail(). I used withStackTraceContaining rather than withMessageContaining on purpose — the error code can surface in the cause chain rather than the top-level message, which is what the old getMessage() + getCause() check was covering. Declared assertj-core as a test dependency in the module pom (the version is already managed in exist-parent).
  • The three new query strings are now text blocks with formatted(baseUrl()).

Module tests stay green (76/76).

joewiz added a commit to joewiz/exist that referenced this pull request Jun 25, 2026
…zation

The native EXPath HTTP Client ignored the @method attribute on http:body, so an
inline body could only be XML-serialized or sent as text -- a binary/base64/hex
inline body could not be sent at all (only http:body/@src, added in eXist-db#6510/eXist-db#6511,
delivered raw bytes). RequestBuilder now honors @method (EXPath HTTP Client 3.1):

- binary / base64: the body's content is base64-decoded and sent as raw bytes.
- hex: the content is hex-decoded and sent as raw bytes.
- text: the content's string value is sent (an element child is serialized as
  its text, not its markup).
- xml / xhtml / html (or no @method): unchanged -- child elements are
  XML-serialized as before.

Malformed base64/hex content raises err:HC005. The string value of body content
is computed by an explicit recursive walk, because the in-memory DOM's
getTextContent does not recurse into element children of a constructed http:body.

Multipart-part @method, JSON/adaptive serialization, and multiple external
$bodies remain follow-ups (eXist-db#6512).

SendRequestFunctionTest gains bodyMethodBinarySendsDecodedBytes,
bodyMethodHexSendsDecodedBytes, and bodyMethodTextSerializesAsText.

Part of eXist-db#6512

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
joewiz added a commit to joewiz/exist that referenced this pull request Jun 25, 2026
Per @reinhapa's review of PR eXist-db#6511:

- Replace the two try/catch + fail() exception tests (HC004, HC005) with
  assertj assertThatExceptionOfType(...).isThrownBy(...).withStackTraceContaining(...).
  withStackTraceContaining preserves the original message-plus-cause matching
  (the error code can surface in the cause chain). Declare assertj-core as a
  test dependency in the module pom (version is managed in exist-parent).
- Convert the three new query strings to text blocks with formatted(baseUrl()).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@joewiz joewiz force-pushed the bugfix/http-client-body-src branch from 79b9cb9 to ed9c6cc Compare June 25, 2026 16:31
@joewiz

joewiz commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

[This response was prompted by Joe, drafted by Claude Code, and reviewed by Joe.]

Rebased onto current develop now that #6503 has merged. This was the most involved of the http-client PRs to rebase, since #6503 reworked the attribute parsing this branch was built on. The @src feature itself (parseBody / resolveBodySource / applyResourceBody) carried over intact; I re-applied it onto #6503's new structure — dropping the old parseAttributes/parseTimeout scaffolding that #6503 replaced with RequestOptionsParser, and in SendRequestFunction resolving http:body/@src to its bytes before the request is built. Module tests stay green (76/76).

@line-o line-o requested a review from a team July 4, 2026 13:36
@line-o line-o added this to v7.0.0 Jul 4, 2026
@line-o line-o added this to the eXist-7.0.0 milestone Jul 4, 2026
@line-o

line-o commented Jul 4, 2026

Copy link
Copy Markdown
Member

Would you rebase a last time now that the flakiness of tests should be resolved (that at least is my understanding).

joewiz and others added 2 commits July 4, 2026 19:17
The native EXPath HTTP Client did not implement http:body/@src (EXPath HTTP
Client spec 3.1): the attribute was parsed away and an empty body was sent, with
no error. RequestBuilder now reads @src, and SendRequestFunction resolves the
linked resource to the request body bytes.

- @src is resolved like fn:doc via SourceFactory, so a database path
  (/db/... or xmldb:), a file: URI, or an http:/classpath location all work; the
  resource bytes are sent verbatim with @media-type as the Content-Type. This
  matches the EXPath reference impl (BaseX), which writes the src resource's raw
  bytes.
- Per the spec, when @src is present the body must have no content and no
  attribute other than media-type -> err:HC004 (the code was already defined but
  never thrown). media-type is mandatory on the body -> err:HC005 (a
  request-validity error; HC004 is reserved for the src/content conflict).

Resolution happens between parse and build (it needs the broker from the query
context), so the built HttpRequest carries the resolved bytes.

SendRequestFunctionTest gains bodySrcSendsResourceContent (a stored db resource
is sent as the body and echoed back), bodySrcWithContentThrowsHC004, and
bodySrcWithoutMediaTypeThrowsHC005.

Closes eXist-db#6510

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per @reinhapa's review of PR eXist-db#6511:

- Replace the two try/catch + fail() exception tests (HC004, HC005) with
  assertj assertThatExceptionOfType(...).isThrownBy(...).withStackTraceContaining(...).
  withStackTraceContaining preserves the original message-plus-cause matching
  (the error code can surface in the cause chain). Declare assertj-core as a
  test dependency in the module pom (version is managed in exist-parent).
- Convert the three new query strings to text blocks with formatted(baseUrl()).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@joewiz joewiz force-pushed the bugfix/http-client-body-src branch from ed9c6cc to 310ce21 Compare July 4, 2026 23:20
@joewiz

joewiz commented Jul 5, 2026

Copy link
Copy Markdown
Member Author

[This response was prompted by Joe, drafted by Claude Code, and reviewed by Joe.]

Rebased onto current develop — CI is green. Thanks, Juri!

@line-o line-o merged commit b7c1b51 into eXist-db:develop Jul 5, 2026
9 checks passed
@github-project-automation github-project-automation Bot moved this to Done in v7.0.0 Jul 5, 2026
joewiz added a commit to joewiz/exist that referenced this pull request Jul 12, 2026
…zation

The native EXPath HTTP Client ignored the @method attribute on http:body, so an
inline body could only be XML-serialized or sent as text -- a binary/base64/hex
inline body could not be sent at all (only http:body/@src, added in eXist-db#6510/eXist-db#6511,
delivered raw bytes). RequestBuilder now honors @method (EXPath HTTP Client 3.1):

- binary / base64: the body's content is base64-decoded and sent as raw bytes.
- hex: the content is hex-decoded and sent as raw bytes.
- text: the content's string value is sent (an element child is serialized as
  its text, not its markup).
- xml / xhtml / html (or no @method): unchanged -- child elements are
  XML-serialized as before.

Malformed base64/hex content raises err:HC005. The string value of body content
is computed by an explicit recursive walk, because the in-memory DOM's
getTextContent does not recurse into element children of a constructed http:body.

Multipart-part @method, JSON/adaptive serialization, and multiple external
$bodies remain follow-ups (eXist-db#6512).

SendRequestFunctionTest gains bodyMethodBinarySendsDecodedBytes,
bodyMethodHexSendsDecodedBytes, and bodyMethodTextSerializesAsText.

Part of eXist-db#6512

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[BUG] http-client has to support the src attribute on http:body

4 participants