[TIKA-4825] carry caller Content-Type across the pipes worker boundary as a detection hint - #3039
[TIKA-4825] carry caller Content-Type across the pipes worker boundary as a detection hint#3039dschmidt wants to merge 3 commits into
Conversation
…y as a detection hint
0795864 to
7afec03
Compare
There was a problem hiding this comment.
Pull request overview
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Carries the caller-supplied Content-Type header across the tika-pipes worker metadata boundary as a soft detection hint (alongside the resource name), so parser routing can use client-provided type information even when no filename is supplied.
Changes:
- Add
PipesWorker.carryCallerHints(...)to copyRESOURCE_NAME_KEYandHttpHeaders.CONTENT_TYPEinto the worker’s freshMetadata. - Update
parseFromTupleto use the new carry method instead of copying only the resource name. - Add unit tests to verify carry behavior and non-carry of
CONTENT_TYPE_USER_OVERRIDE, plus null/blank handling.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/server/PipesWorker.java | Introduces centralized logic to carry caller detection hints (name + Content-Type) into worker metadata. |
| tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/server/PipesWorkerCallerHintsTest.java | Adds tests covering which caller hints are (and aren’t) propagated across the boundary. |
| CHANGES.txt | Documents the new soft-hint behavior for pipes-based endpoints. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| String suppliedName = tupleMetadata.get(TikaCoreProperties.RESOURCE_NAME_KEY); | ||
| if (!StringUtils.isBlank(suppliedName)) { | ||
| target.set(TikaCoreProperties.RESOURCE_NAME_KEY, suppliedName); | ||
| } | ||
| String suppliedContentType = tupleMetadata.get(HttpHeaders.CONTENT_TYPE); | ||
| if (!StringUtils.isBlank(suppliedContentType)) { | ||
| target.set(HttpHeaders.CONTENT_TYPE, suppliedContentType); | ||
| } |
| * The Content-Type is carried only as a soft hint. The unconditional override key | ||
| * must never be carried, or a caller could force any type past detection. | ||
| */ | ||
| @Test | ||
| public void testDoesNotCarryUserOverride() { | ||
| Metadata tuple = new Metadata(); | ||
| tuple.set(TikaCoreProperties.CONTENT_TYPE_USER_OVERRIDE, "image/x-raw-nikon"); | ||
|
|
||
| Metadata target = new Metadata(); | ||
| PipesWorker.carryCallerHints(tuple, target); | ||
|
|
||
| assertNull(target.get(TikaCoreProperties.CONTENT_TYPE_USER_OVERRIDE)); |
| * Pipes now carries the caller-supplied Content-Type across the worker's | ||
| fresh-metadata boundary as a soft detection hint, so /unpack, /async and | ||
| /pipes can route on a client Content-Type (e.g. image/x-raw-nikon for a NEF | ||
| sent without a filename), not only on the filename. The hint only refines | ||
| within the magic-detected type hierarchy; the CONTENT_TYPE_USER_OVERRIDE key | ||
| is deliberately not carried (TIKA-4825). |
|
@tballison @THausherr is 4.0.0 already released? This could be a somewhat breaking change that might be worth getting into the major release if you can still cut a rc2 |
…-Type routing - also assert CONTENT_TYPE_PARSER_OVERRIDE is never carried - mention /unpack/all in CHANGES - testMeta now expects 422 when an explicit Content-Type routes a truncated mock doc to the mock parser; new testMetaNoType keeps the graceful 404 case
|
I don't view this as breaking. It is a good catch, but I'm ok w 4.0.1. Let me know if you disagree. |
|
Just wanted to bring it up, so you can decide because it's a slight behavioral change. Fine with me either way :) |
|
claude has some input. let me know what you think. |
…st, negative-path test, 4.0.1 CHANGES, migration note - CHANGES: move entry to a new Release 4.0.1 section (per maintainer call), list all forked-parse endpoints, use the image/tiff -> image/x-canon-cr2 refinement that works on current main (independent of apache#3037), and note the no-magic case matches existing filename power - test: assert a specializing Content-Type refines detection and a non-specializing/garbage one is ignored (the security boundary) - migration guide: note the 3.x hard-override to 4.x soft-hint change - trim the call-site comment to one line
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Suppressed comments (1)
tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/server/PipesWorker.java:520
- The Javadoc says only
CONTENT_TYPE_USER_OVERRIDEis not carried, butcarryCallerHints()also does not carryCONTENT_TYPE_PARSER_OVERRIDE(and the new unit test explicitly asserts that). Update the Javadoc to mention both override keys to avoid misleading future changes.
* filename). The {@code CONTENT_TYPE_USER_OVERRIDE} key is deliberately NOT carried:
* it short-circuits detection unconditionally and would let a caller force any type.
*
When parsing through tika-pipes, a caller can steer parser selection by supplying a filename, but supplying the correct
Content-Typeheader alone does not work. This affects every forked-parse endpoint (/tika,/meta,/rmeta,/unpack,/async,/pipes), plus tika-grpc and embeddedPipesForkParser.Root cause:
PipesWorker.parseFromTuplebuilds a freshMetadatafor fetch/detection (deliberately isolated from the caller's tuple metadata) and carries onlyRESOURCE_NAME_KEYacross that boundary. The caller'sContent-Type, whichTikaResource.fillMetadatadoes set, is dropped before detection. Core detection would honor it:MimeTypes.detectapplies aContent-Typehint viaapplyHint, keeping it when it equals or specializes the content-detected type.This change carries
HttpHeaders.CONTENT_TYPEacross the boundary alongside the resource name, as a soft hint. Example on current main: a plain TIFF sent withContent-Type: image/x-canon-cr2(a registered sub-class ofimage/tiff) now refines fromimage/tifftoimage/x-canon-cr2, whereas before the header was ignored. This is independent of #3037; raw formats such as NEF benefit once they are registered asimage/tiffsub-types (which #3037 does), but this PR needs nothing from it.Security: only the soft hint is carried, deliberately not
CONTENT_TYPE_USER_OVERRIDE/CONTENT_TYPE_PARSER_OVERRIDE.applyHintkeeps the hint only when it equals or specializes the content-detected type, so a caller can refine within the hierarchy but cannot force an unrelated type. For bytes with no magic everything specializesapplication/octet-stream, so the hint can win there, matching the routing power the filename already had. Detection then overwritesHttpHeaders.CONTENT_TYPEwith the detected type before parsing, so no parser sees an unvalidated caller value.Tests:
PipesWorkerCallerHintsTestcovers the carry (name and Content-Type carried, override keys never carried, null/blank no-ops) and detection (a specializing Content-Type refines, a non-specializing or garbage one is ignored). The migration guide gains a note on the 3.x hard-override to 4.x soft-hint change.Behavior change (targeted for 4.0.1)
Because the caller
Content-Typenow reaches detection, it can change parser selection for any forked-parse request that sends one, wherever the type was previously ignored. Concretely it flipsStackTraceTest.testMeta: a truncatedapplication/mock+xmlPUT to/meta/Authorused to return 404 (truncation handled gracefully as field-not-found); now the explicitContent-Typeroutes the truncated document to the mock parser, which cannot parse the incomplete XML, so the container exception maps to 422 on the bare-field endpoint.testMetanow asserts 422; a newtestMetaNoTypekeeps the graceful 404 case (no forcing type). Per the discussion thread this lands in 4.0.1.