Skip to content

Commit 5c747a4

Browse files
committed
Avoid redundant URI object creation in WebClientUtils
Prior to this commit, WebClientUtils.getRequestDescription() created a new URI object on every invocation. Since the URI constructor includes validation and parsing, which is already performed by the parameter URI object, this was unnecessarily expensive for a logging utility. This commit reuses the original URI's string representation to avoid redundant parsing. Closes gh-36605 Signed-off-by: 박동윤 (Park Dong-Yun) <ehddbs7458@gmail.com>
1 parent e0e7825 commit 5c747a4

2 files changed

Lines changed: 149 additions & 7 deletions

File tree

spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClientUtils.java

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package org.springframework.web.reactive.function.client;
1818

1919
import java.net.URI;
20-
import java.net.URISyntaxException;
2120
import java.util.List;
2221
import java.util.function.Predicate;
2322

@@ -28,7 +27,6 @@
2827
import org.springframework.core.codec.CodecException;
2928
import org.springframework.http.HttpMethod;
3029
import org.springframework.http.ResponseEntity;
31-
import org.springframework.util.StringUtils;
3230

3331
/**
3432
* Internal methods shared between {@link DefaultWebClient} and
@@ -69,16 +67,33 @@ public static <T> Mono<ResponseEntity<List<T>>> mapToEntityList(ClientResponse r
6967
}
7068

7169
/**
72-
* Return a String representation of the request details for logging purposes.
70+
* Return a String representation of the request details for logging purposes
71+
* in "METHOD URI" format.
7372
* @since 6.0.16
7473
*/
7574
public static String getRequestDescription(HttpMethod httpMethod, URI uri) {
76-
if (StringUtils.hasText(uri.getQuery())) {
77-
try {
78-
uri = new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), null, null);
75+
if (uri.getRawQuery() != null) {
76+
StringBuilder sb = new StringBuilder();
77+
if (uri.getScheme() != null) {
78+
sb.append(uri.getScheme()).append(':');
7979
}
80-
catch (URISyntaxException ignored) {
80+
if (uri.getHost() != null) {
81+
sb.append("//");
82+
String host = uri.getHost();
83+
if (host.indexOf(':') >= 0 && !host.startsWith("[") && !host.endsWith("]")) {
84+
sb.append('[').append(host).append(']');
85+
}
86+
else {
87+
sb.append(host);
88+
}
89+
if (uri.getPort() != -1) {
90+
sb.append(':').append(uri.getPort());
91+
}
8192
}
93+
if (uri.getPath() != null) {
94+
sb.append(uri.getPath());
95+
}
96+
return httpMethod.name() + " " + sb;
8297
}
8398
return httpMethod.name() + " " + uri;
8499
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright 2002-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.web.reactive.function.client;
18+
19+
import java.net.URI;
20+
21+
import org.junit.jupiter.api.Test;
22+
23+
import org.springframework.http.HttpMethod;
24+
25+
import static org.assertj.core.api.Assertions.assertThat;
26+
27+
/**
28+
* Tests for {@link WebClientUtils#getRequestDescription}.
29+
*
30+
* @author Park Dong-Yun
31+
*/
32+
class WebClientUtilsTests {
33+
34+
@Test
35+
void stripsQueryParams() {
36+
URI uri = URI.create("https://api.example.com/search?q=test&page=1");
37+
assertThat(WebClientUtils.getRequestDescription(HttpMethod.GET, uri))
38+
.isEqualTo("GET https://api.example.com/search");
39+
}
40+
41+
@Test
42+
void noQueryReturnsAsIs() {
43+
URI uri = URI.create("https://api.example.com/health");
44+
assertThat(WebClientUtils.getRequestDescription(HttpMethod.GET, uri))
45+
.isEqualTo("GET https://api.example.com/health");
46+
}
47+
48+
@Test
49+
void preservesPort() {
50+
URI uri = URI.create("https://api.example.com:8443/path?q=1");
51+
assertThat(WebClientUtils.getRequestDescription(HttpMethod.GET, uri))
52+
.isEqualTo("GET https://api.example.com:8443/path");
53+
}
54+
55+
@Test
56+
void decodesPathWhenStrippingQuery() {
57+
URI uri = URI.create("https://host/hello%20world?q=1");
58+
assertThat(WebClientUtils.getRequestDescription(HttpMethod.GET, uri))
59+
.isEqualTo("GET https://host/hello world");
60+
}
61+
62+
@Test
63+
void stripsUserInfoWithQuery() {
64+
URI uri = URI.create("https://admin:secret@host/api?token=abc");
65+
assertThat(WebClientUtils.getRequestDescription(HttpMethod.GET, uri))
66+
.isEqualTo("GET https://host/api");
67+
}
68+
69+
@Test
70+
void preservesUserInfoWithoutQuery() {
71+
URI uri = URI.create("https://admin:secret@host/api");
72+
assertThat(WebClientUtils.getRequestDescription(HttpMethod.GET, uri))
73+
.isEqualTo("GET https://admin:secret@host/api");
74+
}
75+
76+
@Test
77+
void stripsFragmentWithQuery() {
78+
URI uri = URI.create("https://host/page?q=1#section");
79+
assertThat(WebClientUtils.getRequestDescription(HttpMethod.GET, uri))
80+
.isEqualTo("GET https://host/page");
81+
}
82+
83+
@Test
84+
void preservesFragmentWithoutQuery() {
85+
URI uri = URI.create("https://host/page#section");
86+
assertThat(WebClientUtils.getRequestDescription(HttpMethod.GET, uri))
87+
.isEqualTo("GET https://host/page#section");
88+
}
89+
90+
@Test
91+
void questionMarkInFragmentNotTreatedAsQuery() {
92+
URI uri = URI.create("https://host/page#frag?param=value");
93+
assertThat(WebClientUtils.getRequestDescription(HttpMethod.GET, uri))
94+
.isEqualTo("GET https://host/page#frag?param=value");
95+
}
96+
97+
@Test
98+
void ipv6Host() {
99+
URI uri = URI.create("http://[::1]:8080/path?q=1");
100+
assertThat(WebClientUtils.getRequestDescription(HttpMethod.GET, uri))
101+
.isEqualTo("GET http://[::1]:8080/path");
102+
}
103+
104+
@Test
105+
void opaqueUriUnchanged() {
106+
URI uri = URI.create("mailto:user@example.com?subject=hello");
107+
assertThat(WebClientUtils.getRequestDescription(HttpMethod.GET, uri))
108+
.isEqualTo("GET mailto:user@example.com?subject=hello");
109+
}
110+
111+
@Test
112+
void relativeUri() {
113+
URI uri = URI.create("/api/search?q=test");
114+
assertThat(WebClientUtils.getRequestDescription(HttpMethod.GET, uri))
115+
.isEqualTo("GET /api/search");
116+
}
117+
118+
@Test
119+
void prefixesHttpMethod() {
120+
URI uri = URI.create("https://host/resource?v=1");
121+
assertThat(WebClientUtils.getRequestDescription(HttpMethod.POST, uri))
122+
.startsWith("POST ");
123+
assertThat(WebClientUtils.getRequestDescription(HttpMethod.DELETE, uri))
124+
.startsWith("DELETE ");
125+
}
126+
127+
}

0 commit comments

Comments
 (0)