Copilot commented on code in PR #6296:
URL: https://github.com/apache/shenyu/pull/6296#discussion_r2791587231


##########
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-request-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/request/AiRequestTransformerPlugin.java:
##########
@@ -218,6 +221,28 @@ private static Mono<ServerWebExchange> convertHeader(final 
ServerWebExchange exc
         return Mono.just(exchange);
     }
 
+    private static Mono<ServerWebExchange> rewriteRequestPath(final 
ServerWebExchange exchange, final String aiResponse) {
+        String newPath = extractRequestPathFromAiResponse(aiResponse);
+        if (Objects.isNull(newPath) || newPath.isEmpty()) {
+            return Mono.just(exchange);
+        }
+
+        ServerHttpRequest originalRequest = exchange.getRequest();
+        URI originalUri = originalRequest.getURI();
+
+        URI newUri = originalUri.resolve(newPath);
+
+        ServerHttpRequest newRequest = originalRequest.mutate()
+                .uri(newUri)
+                .build();
+
+        ServerWebExchange newExchange = exchange.mutate()
+                .request(newRequest)
+                .build();
+
+        return Mono.just(newExchange);

Review Comment:
   The extracted path from the AI response is used directly to rewrite the 
request URI without any validation or sanitization. This could introduce 
security vulnerabilities if the AI generates malicious paths. Consider adding 
validation to ensure the extracted path is safe, such as checking for path 
traversal attempts (e.g., "../"), ensuring it starts with "/", and potentially 
restricting the allowed path patterns. Additionally, consider logging the path 
rewrite operation for security auditing purposes.



##########
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-request-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/request/AiRequestTransformerPlugin.java:
##########
@@ -253,6 +278,24 @@ static HttpHeaders extractHeadersFromAiResponse(final 
String aiResponse) {
         return headers;
     }
 
+    private static String extractRequestPathFromAiResponse(final String 
aiResponse) {
+        if (Objects.isNull(aiResponse) || aiResponse.isEmpty()) {
+            return null;
+        }
+        String[] lines = aiResponse.split("\r?\n");
+        if (lines.length == 0) {
+            return null;
+        }
+
+        String startLine = lines[0].trim();
+        String[] parts = startLine.split(" ");
+        if (parts.length < 2) {
+            return null;
+        }
+

Review Comment:
   The extractRequestPathFromAiResponse method assumes the first line of 
aiResponse contains an HTTP request line (e.g., "GET /path HTTP/1.1"), but 
based on the test cases (AiRequestTransformerPluginTest.java:120,128) and the 
extractHeadersFromAiResponse method (line 259), the aiResponse format appears 
to be an HTTP response starting with "HTTP/1.1 200 OK". This creates a format 
mismatch - the method expects to extract parts[1] from a request line format, 
but will actually receive a response line where parts[1] would be the status 
code (e.g., "200"), not a path. This will result in incorrect path extraction. 
The method should be updated to handle the actual HTTP response format or the 
expected format should be clarified.
   ```suggestion
           String startLine = lines[0].trim();
           if (startLine.isEmpty()) {
               return null;
           }
           // If this looks like an HTTP response status line (e.g., "HTTP/1.1 
200 OK"),
           // there is no request path to extract.
           if (startLine.startsWith("HTTP/")) {
               return null;
           }
           // Otherwise, expect a request line: "METHOD /path HTTP/1.1"
           String[] parts = startLine.split("\\s+");
           if (parts.length < 2) {
               return null;
           }
           // Only treat it as a request line if the first token is an HTTP 
method.
           String method = parts[0];
           if (!method.matches("GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD")) {
               return null;
           }
   ```



##########
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-request-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/request/AiRequestTransformerPlugin.java:
##########
@@ -218,6 +221,28 @@ private static Mono<ServerWebExchange> convertHeader(final 
ServerWebExchange exc
         return Mono.just(exchange);
     }
 
+    private static Mono<ServerWebExchange> rewriteRequestPath(final 
ServerWebExchange exchange, final String aiResponse) {
+        String newPath = extractRequestPathFromAiResponse(aiResponse);
+        if (Objects.isNull(newPath) || newPath.isEmpty()) {
+            return Mono.just(exchange);
+        }
+
+        ServerHttpRequest originalRequest = exchange.getRequest();
+        URI originalUri = originalRequest.getURI();
+
+        URI newUri = originalUri.resolve(newPath);

Review Comment:
   The URI.resolve() method may not behave as expected for path rewriting. 
According to RFC 2396, resolve() is designed for relative URI resolution and 
treats paths starting with "/" as absolute paths that replace the entire path 
of the base URI, which may not preserve the scheme and authority correctly. 
Consider using UriUtils.createUri(scheme, authority, path) from the existing 
codebase (similar to FallbackHandler.java:71) to ensure consistent URI 
construction that properly preserves the scheme and authority while replacing 
only the path.



##########
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-request-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/request/AiRequestTransformerPlugin.java:
##########
@@ -115,15 +116,17 @@ protected Mono<Void> doExecute(final ServerWebExchange 
exchange, final ShenyuPlu
 
         AiRequestTransformerTemplate aiRequestTransformerTemplate = new 
AiRequestTransformerTemplate(aiRequestTransformerConfig.getContent(), 
exchange.getRequest());
         ChatClient finalClient = client;
+
         return aiRequestTransformerTemplate.assembleMessage()
-                .flatMap(message -> Mono.fromCallable(() -> 
finalClient.prompt().user(message).call().content())
-                        .subscribeOn(Schedulers.boundedElastic())
+                .flatMap(message -> 
finalClient.prompt().user(message).stream().content()
+                        .collectList()
+                        .map(list -> String.join("", list))
                         .flatMap(aiResponse -> convertHeader(exchange, 
aiResponse)
                                 .flatMap(serverWebExchange -> 
convertBody(serverWebExchange, messageReaders, aiResponse))
-                                    .flatMap(chain::execute)
+                                .flatMap(serverWebExchange -> 
rewriteRequestPath(serverWebExchange, aiResponse))
+                                .flatMap(chain::execute)
                         )
                 );

Review Comment:
   The async refactoring in doExecute method lacks error handling for potential 
failures in the AI streaming operation. Unlike the AiResponseTransformerPlugin 
which has onErrorResume at line 371-375 to handle errors and provide fallback 
behavior, this implementation has no error handling. If the AI call fails 
(e.g., network issues, API errors, stream errors), the entire request will fail 
without any fallback. Consider adding onErrorResume to handle errors 
gracefully, potentially logging the error and either proceeding with the 
original request unchanged or returning an appropriate error response.
   ```suggestion
                   )
                   .onErrorResume(throwable -> {
                       LOG.error("AI request transformation failed, proceeding 
without AI transformation", throwable);
                       return chain.execute(exchange);
                   });
   ```



##########
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-request-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/request/AiRequestTransformerPlugin.java:
##########
@@ -253,6 +278,24 @@ static HttpHeaders extractHeadersFromAiResponse(final 
String aiResponse) {
         return headers;
     }
 
+    private static String extractRequestPathFromAiResponse(final String 
aiResponse) {
+        if (Objects.isNull(aiResponse) || aiResponse.isEmpty()) {
+            return null;
+        }
+        String[] lines = aiResponse.split("\r?\n");
+        if (lines.length == 0) {
+            return null;
+        }
+
+        String startLine = lines[0].trim();
+        String[] parts = startLine.split(" ");
+        if (parts.length < 2) {
+            return null;
+        }
+
+        return parts[1];
+    }

Review Comment:
   The new path rewriting functionality (rewriteRequestPath and 
extractRequestPathFromAiResponse methods) lacks test coverage. The existing 
test file AiRequestTransformerPluginTest.java has tests for convertBodyJson and 
extractHeadersFromAiResponse, but no tests for the new path rewriting feature. 
Consider adding unit tests that verify path extraction from AI responses and 
the path rewriting behavior for various scenarios including edge cases like 
empty paths, malformed responses, and paths with query parameters.



-- 
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