Cache ServerHttpRequest::getMethod in AbstractServerHttpRequest

This commit ensures that the HttpMethod, exposed through
ServerHttpRequest::getMethod, is cached in AbstractServerHttpRequest so
that potentially expensive HTTP method lookups are only done once.

Closes gh-30139
This commit is contained in:
Arjen Poutsma 2023-03-21 11:29:32 +01:00
parent c27a5687dc
commit 37a4e84450
8 changed files with 62 additions and 62 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -54,8 +54,6 @@ import org.springframework.web.util.UriComponentsBuilder;
*/ */
public final class MockServerHttpRequest extends AbstractServerHttpRequest { public final class MockServerHttpRequest extends AbstractServerHttpRequest {
private final HttpMethod httpMethod;
private final MultiValueMap<String, HttpCookie> cookies; private final MultiValueMap<String, HttpCookie> cookies;
@Nullable @Nullable
@ -74,8 +72,7 @@ public final class MockServerHttpRequest extends AbstractServerHttpRequest {
@Nullable InetSocketAddress localAddress, @Nullable InetSocketAddress remoteAddress, @Nullable InetSocketAddress localAddress, @Nullable InetSocketAddress remoteAddress,
@Nullable SslInfo sslInfo, Publisher<? extends DataBuffer> body) { @Nullable SslInfo sslInfo, Publisher<? extends DataBuffer> body) {
super(uri, contextPath, headers); super(httpMethod, uri, contextPath, headers);
this.httpMethod = httpMethod;
this.cookies = cookies; this.cookies = cookies;
this.localAddress = localAddress; this.localAddress = localAddress;
this.remoteAddress = remoteAddress; this.remoteAddress = remoteAddress;
@ -84,11 +81,6 @@ public final class MockServerHttpRequest extends AbstractServerHttpRequest {
} }
@Override
public HttpMethod getMethod() {
return this.httpMethod;
}
@Override @Override
@Nullable @Nullable
public InetSocketAddress getLocalAddress() { public InetSocketAddress getLocalAddress() {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -24,8 +24,10 @@ import java.util.regex.Pattern;
import org.springframework.http.HttpCookie; import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.RequestPath; import org.springframework.http.server.RequestPath;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
@ -49,6 +51,10 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest {
private final HttpHeaders headers; private final HttpHeaders headers;
// TODO: remove @Nullable once deprecated constructors have been removed
@Nullable
private final HttpMethod method;
@Nullable @Nullable
private MultiValueMap<String, String> queryParams; private MultiValueMap<String, String> queryParams;
@ -71,11 +77,35 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest {
* @param contextPath the context path for the request * @param contextPath the context path for the request
* @param headers the headers for the request (as {@link MultiValueMap}) * @param headers the headers for the request (as {@link MultiValueMap})
* @since 5.3 * @since 5.3
* @deprecated since 6.0.8, in favor of {@link #AbstractServerHttpRequest(HttpMethod, URI, String, MultiValueMap)}
*/ */
@Deprecated(since = "6.0.8", forRemoval = true)
public AbstractServerHttpRequest(URI uri, @Nullable String contextPath, MultiValueMap<String, String> headers) { public AbstractServerHttpRequest(URI uri, @Nullable String contextPath, MultiValueMap<String, String> headers) {
this.uri = uri; this.uri = uri;
this.path = RequestPath.parse(uri, contextPath); this.path = RequestPath.parse(uri, contextPath);
this.headers = HttpHeaders.readOnlyHttpHeaders(headers); this.headers = HttpHeaders.readOnlyHttpHeaders(headers);
this.method = null;
}
/**
* Constructor with the method, URI and headers for the request.
* @param method the HTTP method for the request
* @param uri the URI for the request
* @param contextPath the context path for the request
* @param headers the headers for the request (as {@link MultiValueMap})
* @since 6.0.8
*/
public AbstractServerHttpRequest(HttpMethod method, URI uri, @Nullable String contextPath,
MultiValueMap<String, String> headers) {
Assert.notNull(method, "Method must not be null");
Assert.notNull(uri, "Uri must not be null");
Assert.notNull(headers, "Headers must not be null");
this.method = method;
this.uri = uri;
this.path = RequestPath.parse(uri, contextPath);
this.headers = HttpHeaders.readOnlyHttpHeaders(headers);
} }
/** /**
@ -83,11 +113,14 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest {
* @param uri the URI for the request * @param uri the URI for the request
* @param contextPath the context path for the request * @param contextPath the context path for the request
* @param headers the headers for the request (as {@link HttpHeaders}) * @param headers the headers for the request (as {@link HttpHeaders})
* @deprecated since 6.0.8, in favor of {@link #AbstractServerHttpRequest(HttpMethod, URI, String, MultiValueMap)}
*/ */
@Deprecated(since = "6.0.8", forRemoval = true)
public AbstractServerHttpRequest(URI uri, @Nullable String contextPath, HttpHeaders headers) { public AbstractServerHttpRequest(URI uri, @Nullable String contextPath, HttpHeaders headers) {
this.uri = uri; this.uri = uri;
this.path = RequestPath.parse(uri, contextPath); this.path = RequestPath.parse(uri, contextPath);
this.headers = HttpHeaders.readOnlyHttpHeaders(headers); this.headers = HttpHeaders.readOnlyHttpHeaders(headers);
this.method = null;
} }
@ -112,6 +145,18 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest {
return null; return null;
} }
@Override
public HttpMethod getMethod() {
// TODO: remove null check once deprecated constructors have been removed
if (this.method != null) {
return this.method;
}
else {
throw new IllegalStateException("No HttpMethod provided in constructor, " +
"and AbstractServerHttpRequest::getMethod not overridden");
}
}
@Override @Override
public URI getURI() { public URI getURI() {
return this.uri; return this.uri;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -177,8 +177,6 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
private static class MutatedServerHttpRequest extends AbstractServerHttpRequest { private static class MutatedServerHttpRequest extends AbstractServerHttpRequest {
private final HttpMethod method;
@Nullable @Nullable
private final SslInfo sslInfo; private final SslInfo sslInfo;
@ -194,19 +192,13 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
HttpMethod method, @Nullable SslInfo sslInfo, @Nullable InetSocketAddress remoteAddress, HttpMethod method, @Nullable SslInfo sslInfo, @Nullable InetSocketAddress remoteAddress,
HttpHeaders headers, Flux<DataBuffer> body, ServerHttpRequest originalRequest) { HttpHeaders headers, Flux<DataBuffer> body, ServerHttpRequest originalRequest) {
super(uri, contextPath, headers); super(method, uri, contextPath, headers);
this.method = method;
this.remoteAddress = (remoteAddress != null ? remoteAddress : originalRequest.getRemoteAddress()); this.remoteAddress = (remoteAddress != null ? remoteAddress : originalRequest.getRemoteAddress());
this.sslInfo = (sslInfo != null ? sslInfo : originalRequest.getSslInfo()); this.sslInfo = (sslInfo != null ? sslInfo : originalRequest.getSslInfo());
this.body = body; this.body = body;
this.originalRequest = originalRequest; this.originalRequest = originalRequest;
} }
@Override
public HttpMethod getMethod() {
return this.method;
}
@Override @Override
protected MultiValueMap<String, HttpCookie> initCookies() { protected MultiValueMap<String, HttpCookie> initCookies() {
return this.originalRequest.getCookies(); return this.originalRequest.getCookies();

View File

@ -63,17 +63,15 @@ class ReactorNetty2ServerHttpRequest extends AbstractServerHttpRequest {
private final Netty5DataBufferFactory bufferFactory; private final Netty5DataBufferFactory bufferFactory;
private final HttpMethod method;
public ReactorNetty2ServerHttpRequest(HttpServerRequest request, Netty5DataBufferFactory bufferFactory) public ReactorNetty2ServerHttpRequest(HttpServerRequest request, Netty5DataBufferFactory bufferFactory)
throws URISyntaxException { throws URISyntaxException {
super(initUri(request), "", new Netty5HeadersAdapter(request.requestHeaders())); super(HttpMethod.valueOf(request.method().name()), initUri(request), "",
new Netty5HeadersAdapter(request.requestHeaders()));
Assert.notNull(bufferFactory, "DataBufferFactory must not be null"); Assert.notNull(bufferFactory, "DataBufferFactory must not be null");
this.request = request; this.request = request;
this.bufferFactory = bufferFactory; this.bufferFactory = bufferFactory;
this.method = HttpMethod.valueOf(request.method().name());
} }
private static URI initUri(HttpServerRequest request) throws URISyntaxException { private static URI initUri(HttpServerRequest request) throws URISyntaxException {
@ -142,11 +140,6 @@ class ReactorNetty2ServerHttpRequest extends AbstractServerHttpRequest {
return uri; return uri;
} }
@Override
public HttpMethod getMethod() {
return this.method;
}
@Override @Override
protected MultiValueMap<String, HttpCookie> initCookies() { protected MultiValueMap<String, HttpCookie> initCookies() {
MultiValueMap<String, HttpCookie> cookies = new LinkedMultiValueMap<>(); MultiValueMap<String, HttpCookie> cookies = new LinkedMultiValueMap<>();

View File

@ -61,17 +61,15 @@ class ReactorServerHttpRequest extends AbstractServerHttpRequest {
private final NettyDataBufferFactory bufferFactory; private final NettyDataBufferFactory bufferFactory;
private final HttpMethod method;
public ReactorServerHttpRequest(HttpServerRequest request, NettyDataBufferFactory bufferFactory) public ReactorServerHttpRequest(HttpServerRequest request, NettyDataBufferFactory bufferFactory)
throws URISyntaxException { throws URISyntaxException {
super(initUri(request), "", new NettyHeadersAdapter(request.requestHeaders())); super(HttpMethod.valueOf(request.method().name()), initUri(request), "",
new NettyHeadersAdapter(request.requestHeaders()));
Assert.notNull(bufferFactory, "DataBufferFactory must not be null"); Assert.notNull(bufferFactory, "DataBufferFactory must not be null");
this.request = request; this.request = request;
this.bufferFactory = bufferFactory; this.bufferFactory = bufferFactory;
this.method = HttpMethod.valueOf(request.method().name());
} }
private static URI initUri(HttpServerRequest request) throws URISyntaxException { private static URI initUri(HttpServerRequest request) throws URISyntaxException {
@ -112,10 +110,6 @@ class ReactorServerHttpRequest extends AbstractServerHttpRequest {
return uri; return uri;
} }
@Override
public HttpMethod getMethod() {
return this.method;
}
@Override @Override
protected MultiValueMap<String, HttpCookie> initCookies() { protected MultiValueMap<String, HttpCookie> initCookies() {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -90,7 +90,8 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest {
AsyncContext asyncContext, String servletPath, DataBufferFactory bufferFactory, int bufferSize) AsyncContext asyncContext, String servletPath, DataBufferFactory bufferFactory, int bufferSize)
throws IOException, URISyntaxException { throws IOException, URISyntaxException {
super(initUri(request), request.getContextPath() + servletPath, initHeaders(headers, request)); super(HttpMethod.valueOf(request.getMethod()), initUri(request), request.getContextPath() + servletPath,
initHeaders(headers, request));
Assert.notNull(bufferFactory, "'bufferFactory' must not be null"); Assert.notNull(bufferFactory, "'bufferFactory' must not be null");
Assert.isTrue(bufferSize > 0, "'bufferSize' must be greater than 0"); Assert.isTrue(bufferSize > 0, "'bufferSize' must be greater than 0");
@ -162,11 +163,6 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest {
return (headers != null ? headers : headerValues); return (headers != null ? headers : headerValues);
} }
@Override
public HttpMethod getMethod() {
return HttpMethod.valueOf(this.request.getMethod());
}
@Override @Override
protected MultiValueMap<String, HttpCookie> initCookies() { protected MultiValueMap<String, HttpCookie> initCookies() {
MultiValueMap<String, HttpCookie> httpCookies = new LinkedMultiValueMap<>(); MultiValueMap<String, HttpCookie> httpCookies = new LinkedMultiValueMap<>();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -64,7 +64,8 @@ class UndertowServerHttpRequest extends AbstractServerHttpRequest {
public UndertowServerHttpRequest(HttpServerExchange exchange, DataBufferFactory bufferFactory) public UndertowServerHttpRequest(HttpServerExchange exchange, DataBufferFactory bufferFactory)
throws URISyntaxException { throws URISyntaxException {
super(initUri(exchange), "", new UndertowHeadersAdapter(exchange.getRequestHeaders())); super(HttpMethod.valueOf(exchange.getRequestMethod().toString()), initUri(exchange), "",
new UndertowHeadersAdapter(exchange.getRequestHeaders()));
this.exchange = exchange; this.exchange = exchange;
this.body = new RequestBodyPublisher(exchange, bufferFactory); this.body = new RequestBodyPublisher(exchange, bufferFactory);
this.body.registerListeners(exchange); this.body.registerListeners(exchange);
@ -78,11 +79,6 @@ class UndertowServerHttpRequest extends AbstractServerHttpRequest {
return new URI(requestUriAndQuery); return new URI(requestUriAndQuery);
} }
@Override
public HttpMethod getMethod() {
return HttpMethod.valueOf(this.exchange.getRequestMethod().toString());
}
@Override @Override
protected MultiValueMap<String, HttpCookie> initCookies() { protected MultiValueMap<String, HttpCookie> initCookies() {
MultiValueMap<String, HttpCookie> cookies = new LinkedMultiValueMap<>(); MultiValueMap<String, HttpCookie> cookies = new LinkedMultiValueMap<>();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -55,8 +55,6 @@ import org.springframework.web.util.UriComponentsBuilder;
*/ */
public final class MockServerHttpRequest extends AbstractServerHttpRequest { public final class MockServerHttpRequest extends AbstractServerHttpRequest {
private final HttpMethod httpMethod;
private final MultiValueMap<String, HttpCookie> cookies; private final MultiValueMap<String, HttpCookie> cookies;
@Nullable @Nullable
@ -75,8 +73,7 @@ public final class MockServerHttpRequest extends AbstractServerHttpRequest {
@Nullable InetSocketAddress localAddress, @Nullable InetSocketAddress remoteAddress, @Nullable InetSocketAddress localAddress, @Nullable InetSocketAddress remoteAddress,
@Nullable SslInfo sslInfo, Publisher<? extends DataBuffer> body) { @Nullable SslInfo sslInfo, Publisher<? extends DataBuffer> body) {
super(uri, contextPath, headers); super(httpMethod, uri, contextPath, headers);
this.httpMethod = httpMethod;
this.cookies = cookies; this.cookies = cookies;
this.localAddress = localAddress; this.localAddress = localAddress;
this.remoteAddress = remoteAddress; this.remoteAddress = remoteAddress;
@ -85,11 +82,6 @@ public final class MockServerHttpRequest extends AbstractServerHttpRequest {
} }
@Override
public HttpMethod getMethod() {
return this.httpMethod;
}
@Override @Override
@Nullable @Nullable
public InetSocketAddress getLocalAddress() { public InetSocketAddress getLocalAddress() {