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


##########
shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/McpServerPlugin.java:
##########
@@ -579,11 +619,34 @@ private Mono<Void> handleMessageEndpoint(final 
ServerWebExchange exchange,
      * @param exchange the server web exchange
      */
     private void setCorsHeaders(final ServerWebExchange exchange) {
-        exchange.getResponse().getHeaders().set("Access-Control-Allow-Origin", 
"*");
-        exchange.getResponse().getHeaders().set("Access-Control-Allow-Headers",
-                "Content-Type, Mcp-Session-Id, Authorization, Last-Event-ID, 
Mcp-Protocol-Version");
-        exchange.getResponse().getHeaders().set("Access-Control-Allow-Methods",
-                "GET, POST, OPTIONS");
+        exchange.getResponse().getHeaders().set("Access-Control-Allow-Origin", 
resolveAllowOrigin(exchange));
+        
exchange.getResponse().getHeaders().set("Access-Control-Allow-Headers", 
resolveAllowHeaders(exchange));
+        
exchange.getResponse().getHeaders().set("Access-Control-Allow-Methods", 
CORS_ALLOW_METHODS);
+        exchange.getResponse().getHeaders().set("Vary", "Origin, 
Access-Control-Request-Headers");
+    }
+
+    private String resolveAllowOrigin(final ServerWebExchange exchange) {
+        final String origin = 
exchange.getRequest().getHeaders().getFirst("Origin");
+        return Objects.nonNull(origin) && !origin.isBlank() ? origin : "*";
+    }
+
+    private String resolveAllowHeaders(final ServerWebExchange exchange) {
+        final Set<String> allowedHeaders = new LinkedHashSet<>();
+        final String allowHeaders = 
Objects.nonNull(configuredCorsAllowHeaders) && 
!configuredCorsAllowHeaders.isBlank()
+                ? configuredCorsAllowHeaders : CORS_FALLBACK_ALLOW_HEADERS;
+        for (String header : allowHeaders.split(",")) {
+            allowedHeaders.add(header.trim());
+        }
+        final String requestedHeaders = 
exchange.getRequest().getHeaders().getFirst("Access-Control-Request-Headers");
+        if (Objects.nonNull(requestedHeaders) && !requestedHeaders.isBlank()) {
+            for (String requestedHeader : requestedHeaders.split(",")) {
+                final String header = requestedHeader.trim();
+                if (!header.isEmpty()) {
+                    allowedHeaders.add(header);
+                }

Review Comment:
   This logic appends all client-supplied `Access-Control-Request-Headers` into 
the allow-list, which effectively allows any requested header and makes 
`shenyu.cross.allowedHeaders` non-enforcing. Consider responding with only the 
configured allow-list (or intersecting configured vs requested) so disallowed 
requested headers fail preflight as intended.
   ```suggestion
               final String trimmed = header.trim();
               if (!trimmed.isEmpty()) {
                   allowedHeaders.add(trimmed);
   ```



##########
shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/McpServerPlugin.java:
##########
@@ -579,11 +619,34 @@ private Mono<Void> handleMessageEndpoint(final 
ServerWebExchange exchange,
      * @param exchange the server web exchange
      */
     private void setCorsHeaders(final ServerWebExchange exchange) {
-        exchange.getResponse().getHeaders().set("Access-Control-Allow-Origin", 
"*");
-        exchange.getResponse().getHeaders().set("Access-Control-Allow-Headers",
-                "Content-Type, Mcp-Session-Id, Authorization, Last-Event-ID, 
Mcp-Protocol-Version");
-        exchange.getResponse().getHeaders().set("Access-Control-Allow-Methods",
-                "GET, POST, OPTIONS");
+        exchange.getResponse().getHeaders().set("Access-Control-Allow-Origin", 
resolveAllowOrigin(exchange));
+        
exchange.getResponse().getHeaders().set("Access-Control-Allow-Headers", 
resolveAllowHeaders(exchange));
+        
exchange.getResponse().getHeaders().set("Access-Control-Allow-Methods", 
CORS_ALLOW_METHODS);
+        exchange.getResponse().getHeaders().set("Vary", "Origin, 
Access-Control-Request-Headers");

Review Comment:
   `set("Vary", ...)` overwrites any existing `Vary` header that may already be 
set by the framework (e.g., `Accept-Encoding`), which can break caching 
semantics. Prefer adding/merging the `Vary` values instead of replacing the 
header entirely.
   ```suggestion
   
           // Merge CORS-related Vary values with any existing Vary header 
entries
           final List<String> existingVary = 
exchange.getResponse().getHeaders().getVary();
           final Set<String> varyValues = new LinkedHashSet<>(existingVary);
           varyValues.add("Origin");
           varyValues.add("Access-Control-Request-Headers");
           exchange.getResponse().getHeaders().setVary(new 
java.util.ArrayList<>(varyValues));
   ```



##########
shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/transport/ShenyuStreamableHttpServerTransportProvider.java:
##########
@@ -643,6 +661,40 @@ private Object createJsonRpcError(final Object id, final 
int code, final String
         return response;
     }
 
+    private ServerResponse.BodyBuilder applyCorsHeaders(final ServerRequest 
request,
+                                                        final 
ServerResponse.BodyBuilder builder,
+                                                        final String 
allowMethods) {
+        return builder
+                .header("Access-Control-Allow-Origin", 
resolveAllowOrigin(request))
+                .header("Access-Control-Allow-Headers", 
resolveAllowHeaders(request))
+                .header("Access-Control-Allow-Methods", allowMethods)
+                .header("Vary", "Origin, Access-Control-Request-Headers");
+    }
+
+    private String resolveAllowOrigin(final ServerRequest request) {
+        final String origin = request.headers().firstHeader("Origin");
+        return Objects.nonNull(origin) && !origin.isBlank() ? origin : "*";
+    }
+
+    private String resolveAllowHeaders(final ServerRequest request) {
+        final Set<String> allowedHeaders = new LinkedHashSet<>();
+        final String allowHeaders = 
Objects.nonNull(configuredCorsAllowHeaders) && 
!configuredCorsAllowHeaders.isBlank()
+                ? configuredCorsAllowHeaders : CORS_FALLBACK_ALLOW_HEADERS;
+        for (String header : allowHeaders.split(",")) {
+            allowedHeaders.add(header.trim());
+        }
+        final String requestedHeaders = 
request.headers().firstHeader("Access-Control-Request-Headers");
+        if (Objects.nonNull(requestedHeaders) && !requestedHeaders.isBlank()) {
+            for (String requestedHeader : requestedHeaders.split(",")) {
+                final String header = requestedHeader.trim();
+                if (!header.isEmpty()) {
+                    allowedHeaders.add(header);
+                }
+            }

Review Comment:
   This logic unions the configured allow-list with all client-supplied 
`Access-Control-Request-Headers`, which effectively allows any requested header 
and makes the configuration non-enforcing. Consider returning only the 
configured allow-list (or intersecting configured vs requested) so disallowed 
requested headers fail preflight.
   ```suggestion
               final String trimmed = header.trim();
               if (!trimmed.isEmpty()) {
                   allowedHeaders.add(trimmed);
               }
           }
           final String requestedHeaders = 
request.headers().firstHeader("Access-Control-Request-Headers");
           if (Objects.nonNull(requestedHeaders) && 
!requestedHeaders.isBlank()) {
               final Set<String> effectiveHeaders = new LinkedHashSet<>();
               for (String requestedHeader : requestedHeaders.split(",")) {
                   final String header = requestedHeader.trim();
                   if (!header.isEmpty() && allowedHeaders.contains(header)) {
                       effectiveHeaders.add(header);
                   }
               }
               if (!effectiveHeaders.isEmpty()) {
                   return String.join(", ", effectiveHeaders);
               }
   ```



##########
shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/transport/ShenyuStreamableHttpServerTransportProvider.java:
##########
@@ -195,18 +221,12 @@ public Mono<ServerResponse> handleUnifiedEndpoint(final 
ServerRequest request) {
         }
         if ("OPTIONS".equalsIgnoreCase(request.methodName())) {
             // Handle CORS preflight requests
-            return ServerResponse.ok()
-                    .header("Access-Control-Allow-Origin", "*")
-                    .header("Access-Control-Allow-Headers", "Content-Type, 
Mcp-Session-Id, Authorization, Mcp-Protocol-Version")
-                    .header("Access-Control-Allow-Methods", "GET, POST, 
OPTIONS")
+            return applyCorsHeaders(request, ServerResponse.ok(), 
CORS_ALLOW_METHODS)
                     .header("Access-Control-Max-Age", "3600")
                     .build();

Review Comment:
   This transport returns 405 for GET, but the CORS preflight response 
advertises `Access-Control-Allow-Methods: GET, POST, OPTIONS`. Align the 
advertised methods with what the endpoint actually supports (likely `POST, 
OPTIONS`) to avoid misleading clients and incorrect CORS behavior.



##########
shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/McpServerPlugin.java:
##########
@@ -276,6 +303,19 @@ private boolean isSseProtocol(final String uri) {
         return uri.contains(SSE_PATH) || uri.endsWith(SSE_PATH) || 
uri.endsWith(MESSAGE_ENDPOINT);
     }
 
+    /**
+     * Handles CORS preflight (OPTIONS) requests.
+     *
+     * @param exchange the server web exchange
+     * @return a Mono representing completion
+     */
+    private Mono<Void> handleCorsPreflight(final ServerWebExchange exchange) {
+        exchange.getResponse().setStatusCode(HttpStatus.OK);
+        setCorsHeaders(exchange);
+        exchange.getResponse().getHeaders().set("Access-Control-Max-Age", 
"3600");
+        return exchange.getResponse().setComplete();

Review Comment:
   CORS behavior was added/changed (preflight handling + configurable allowed 
headers), but there are no corresponding tests covering OPTIONS requests and 
the resulting `Access-Control-*` headers. Please add unit/integration tests 
that assert the preflight response headers for both configured and default 
allowed-headers scenarios.



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