From d86bf8b2056429edf5494456cffcb2b243331c49 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Mon, 2 Sep 2024 14:11:52 +0200 Subject: [PATCH] Align RouterFunctions resource handling Closes: gh-33434 --- .../server/PathResourceLookupFunction.java | 107 +++++++++++++++--- .../function/PathResourceLookupFunction.java | 104 +++++++++++++++-- 2 files changed, 186 insertions(+), 25 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java index 86e31573887..b5ee5b46967 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.web.reactive.function.server; import java.io.IOException; import java.io.UncheckedIOException; +import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.function.Function; @@ -30,6 +31,7 @@ import org.springframework.http.server.PathContainer; import org.springframework.util.Assert; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; +import org.springframework.web.util.UriUtils; import org.springframework.web.util.pattern.PathPattern; import org.springframework.web.util.pattern.PathPatternParser; @@ -63,13 +65,17 @@ class PathResourceLookupFunction implements FunctionThe default implementation replaces: + * + */ + protected String processPath(String path) { + path = StringUtils.replace(path, "\\", "/"); + path = cleanDuplicateSlashes(path); + return cleanLeadingSlash(path); + } + + private String cleanDuplicateSlashes(String path) { + StringBuilder sb = null; + char prev = 0; + for (int i = 0; i < path.length(); i++) { + char curr = path.charAt(i); + try { + if (curr == '/' && prev == '/') { + if (sb == null) { + sb = new StringBuilder(path.substring(0, i)); + } + continue; + } + if (sb != null) { + sb.append(path.charAt(i)); + } + } + finally { + prev = curr; + } + } + return (sb != null ? sb.toString() : path); + } + + private String cleanLeadingSlash(String path) { boolean slash = false; for (int i = 0; i < path.length(); i++) { if (path.charAt(i) == '/') { @@ -94,8 +140,7 @@ class PathResourceLookupFunction implements Function> { @@ -62,13 +66,17 @@ class PathResourceLookupFunction implements FunctionThe default implementation replaces: + *
    + *
  • Backslash with forward slash. + *
  • Duplicate occurrences of slash with a single slash. + *
  • Any combination of leading slash and control characters (00-1F and 7F) + * with a single "/" or "". For example {@code " / // foo/bar"} + * becomes {@code "/foo/bar"}. + *
+ */ + protected String processPath(String path) { + path = StringUtils.replace(path, "\\", "/"); + path = cleanDuplicateSlashes(path); + return cleanLeadingSlash(path); + } + + private String cleanDuplicateSlashes(String path) { + StringBuilder sb = null; + char prev = 0; + for (int i = 0; i < path.length(); i++) { + char curr = path.charAt(i); + try { + if ((curr == '/') && (prev == '/')) { + if (sb == null) { + sb = new StringBuilder(path.substring(0, i)); + } + continue; + } + if (sb != null) { + sb.append(path.charAt(i)); + } + } + finally { + prev = curr; + } + } + return sb != null ? sb.toString() : path; + } + + private String cleanLeadingSlash(String path) { boolean slash = false; for (int i = 0; i < path.length(); i++) { if (path.charAt(i) == '/') { @@ -93,8 +141,7 @@ class PathResourceLookupFunction implements Function