Skip to content

Accept Mojolicious requests, responses and transactions in validate - #4

Merged
plicease merged 4 commits into
mainfrom
graham/mojo
Oct 14, 2026
Merged

plicease merged 4 commits into
mainfrom
graham/mojo

Conversation

@plicease

Copy link
Copy Markdown
Member

Closes #2.

validate now accepts Mojolicious objects as well as the existing hashes:

$humbug->validate(request => $tx->req, response => $tx->res);
$humbug->validate($tx);                                  # Mojo::Transaction::HTTP
$humbug->validate($tx, validate => { response => 0 });

The conversion lives in OpenAPI::Humbug::Adapter::Mojo, documented as internal. It turns the objects into the existing request/response hashes, which then go through the usual argument checks and encoding. The sidecar, wire protocol and transport are unchanged.

Behaviour (documented under MOJOLICIOUS in the POD)

  • Optional: Mojolicious is never loaded by this module. The adapter is loaded only when a Mojo object is passed, and doesn't load Mojolicious itself. In META.json it is a runtime suggests and a test recommends. A test hides Mojolicious and checks that hashref callers work and nothing Mojo-related gets loaded.
  • Read-only: Mojo objects are never changed. Methods that would change them (fix_headers, build_body, to_string, get_*_chunk) are not called. A test compares Data::Dumper snapshots before and after and checks that no finish event fires. I checked that it catches a fix_headers call.
  • URL: the effective absolute URL is $req->url->to_abs, without fragment or credentials. A relative URL is resolved only against the base Mojo already has (on the server, from the connection and Host). If there's no scheme and host it is an argument error, not a guess.
  • Headers: every line is sent, and repeated lines (Set-Cookie) stay separate and in order. Two things Mojo loses before we see the message are documented: the relative order of lines with different names (Mojo sorts names), and the original casing of common names.
  • Body: the bytes Mojo holds are used, never re-serialized. A body still being received, a streamed (dynamic) body, or a multipart body is an argument error. Mojo keeps multipart only as parsed parts, so there are no original bytes to send.
  • Absent vs empty: Mojo doesn't record the difference, so RFC 9112 §6.3 decides.
    • Request: has a body if it has data, Content-Length or Transfer-Encoding.
    • Response: no body for HEAD (when the request is known), 1xx, 204 or 304; any other response has a body, possibly empty.
  • validate($tx): the response is included only if it has a status code and Mojo has finished it (fully received, or fully sent on the server). Otherwise only the request is checked. Asking for the response with validate => { response => 1 } is then an argument error. On the server before sending (e.g. after_dispatch), pass request/response explicitly.

Tests

  • t/openapi_humbug__mojo.t: in-memory against the fake transport, checking the exact wire form. Covers request-only, request plus response, mixed hash/Mojo, the transaction shorthand, unfinished and partially received transactions, repeated headers, binary/empty/absent bodies, non-ASCII header values, relative/absolute URLs and the missing-base failure, objects that can't be converted, and objects left unchanged.
  • t/openapi_humbug__no_mojo.t: hashref callers with Mojolicious hidden.
  • t/integration.t: a Mojolicious app and user agent against the real humbug (skipped without Mojolicious).

Build

dist.ini sets diag = -EV. Without it the bundle adds BEGIN { eval q{ use EV; } } to the generated t/00_diag.t once Mojolicious appears in the prereqs, and that line fails the ProhibitStringyEval critic policy.

🤖 Generated with Claude Code

plicease and others added 2 commits October 14, 2026 15:22
validate now takes a Mojo::Message::Request and/or Mojo::Message::Response
in place of the request and response hashes, or a Mojo::Transaction::HTTP
in place of both. The private OpenAPI::Humbug::Adapter::Mojo converts them
to the existing hashes, which are then validated as before; it is loaded
only when a Mojo object is passed, and Mojolicious stays optional
(suggested). The Mojo objects are only read, never changed.

Closes #2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
maint/cip-before-install installs the current stable Go and builds humbug
from go-humbug's default branch into /home/cip/bin; linux.yml passes
OPENAPI_HUMBUG_EXECUTABLE to the tests through CIP_ENV. cip's own
"cip pkg golang" installs Go 1.19.2, too old for go-humbug
(uperl/cip#15).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copy link
Copy Markdown
Member Author

One small question about URL credentials in OpenAPI::Humbug::Adapter::Mojo::_url: the POD says the effective URL excludes credentials, and the test covers https://user:secret@api.example.test/p expecting https://api.example.test/p. The implementation explicitly clears the fragment, but I don't see it explicitly clearing userinfo. Does Mojo::URL->to_abs/to_string guarantee credentials are omitted here, or are we relying on incidental behavior? If this is guaranteed and the test passes, no change needed; otherwise I'd prefer to remove userinfo explicitly so the behavior is intentional. Not a blocker.

Mojo::URL->to_string documents that it leaves out userinfo; clear it on
the copy anyway so that does not depend on which method is called.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@plicease

Copy link
Copy Markdown
Member Author

Good question. It was guaranteed: the Mojo::URL docs for to_string say "Note that userinfo will not be included for security reasons", with the example Mojo::URL->new('http://daniel:s3cret@mojolicious.org')->to_string giving http://mojolicious.org. to_unsafe_string exists for the case where you want them.

Still, I agree it's better to be intentional, so 3f9a3e2 clears it explicitly. _url now calls $url->userinfo(undef) next to fragment(undef), with a comment explaining why. That way it doesn't depend on which stringification method is used. to_abs returns a copy, so the caller's URL keeps its credentials, and the test now checks that as well.

@plicease
plicease merged commit e04b81c into main Oct 14, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Mojolicious messages and transactions in validate()

1 participant