oscerd commented on code in PR #143:
URL: https://github.com/apache/camel-kit/pull/143#discussion_r4441957532


##########
knowledge/mcp/src/main/java/org/apache/camel/kit/knowledge/mcp/IndexDownloader.java:
##########
@@ -568,9 +603,41 @@ private InputStream openStream(URI uri) throws 
IOException, InterruptedException
     }
 
     private HttpResponse<InputStream> send(HttpRequest request) throws 
IOException, InterruptedException {
+        final int maxRedirects = 5;
         long deadline = System.nanoTime() + 
request.timeout().orElseThrow().toNanos();
-        return httpClient.send(request, info -> 
HttpResponse.BodySubscribers.mapping(
-                HttpResponse.BodySubscribers.ofPublisher(), publisher -> new 
TimedBody(publisher, deadline)));
+        HttpRequest current = request;
+        for (int hop = 0;; hop++) {
+            HttpResponse<InputStream> response = httpClient.send(current, info 
-> HttpResponse.BodySubscribers.mapping(
+                    HttpResponse.BodySubscribers.ofPublisher(), publisher -> 
new TimedBody(publisher, deadline)));
+            int status = response.statusCode();
+            if (status != 301 && status != 302 && status != 303 && status != 
307 && status != 308) {
+                return response;
+            }
+            // Validate the redirect target's transport BEFORE following it; a 
304 is not a redirect and
+            // is returned above for the conditional-manifest path.
+            String target = 
response.headers().firstValue("Location").orElse(null);
+            response.body().close();
+            if (hop >= maxRedirects) {
+                throw new IOException("Too many index redirects starting from 
" + request.uri());
+            }
+            if (target == null) {
+                throw new IOException("Index redirect without a Location 
header from " + current.uri());
+            }
+            URI next;
+            try {
+                next = current.uri().resolve(target);
+            } catch (IllegalArgumentException e) {
+                throw new IOException("Invalid index redirect target: " + 
target, e);
+            }
+            requireFetchableUri(next);
+            long remaining = deadline - System.nanoTime();
+            if (remaining <= 0) {
+                throw new HttpTimeoutException("Index redirect handling timed 
out from " + request.uri());
+            }
+            HttpRequest.Builder builder = 
HttpRequest.newBuilder(next).timeout(Duration.ofNanos(remaining)).GET();
+            current.headers().firstValue("Accept").ifPresent(accept -> 
builder.header("Accept", accept));
+            current = builder.build();

Review Comment:
   Good catch — the manual redirect loop in 182dcc9 rebuilt the hop request 
with `Accept` only, so the `If-None-Match` that `fetchManifest()` sets was lost 
on the first hop and every redirected resolution re-downloaded. Fixed in 
b1ac1242: `send()` now re-sends `If-None-Match` together with `Accept` on every 
validated hop — an explicit list of the two request headers the downloader 
sets; nothing else crosses a hop.
   
   Regression test `manifestRedirectKeepsConditionalRevalidation`: a loopback 
redirector 302s every path to the `HttpFixture` backend; the first resolve 
installs v1 (200, `ETag: "v1"`); the second resolve must reach the target with 
`If-None-Match: "v1"`, receive 304, reuse the installed directory and leave the 
download count at 1. It fails on 182dcc9 (`expected: <"v1"> but was: <null>`) 
and passes with the fix.
   
   `IndexDownloaderTest` 68 and `IndexDownloaderTimeoutTest` 10 pass 
(knowledge/mcp: 112 passed, 4 existing skips); `-Psourcecheck validate` clean; 
full reactor `clean install -DskipTests` green.
   
   _AI-generated by Claude Code (Claude Fable 5.1) on behalf of @oscerd._



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to