diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java index e897ae5aa80..e25422230ee 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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,7 +18,6 @@ package org.springframework.test.web.servlet.htmlunit; import java.io.File; import java.io.IOException; -import java.net.URL; import java.net.URLDecoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -32,7 +31,6 @@ import java.util.Map; import java.util.Set; import java.util.StringTokenizer; -import com.gargoylesoftware.htmlunit.CookieManager; import com.gargoylesoftware.htmlunit.FormEncodingType; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.WebRequest; @@ -69,6 +67,7 @@ import org.springframework.web.util.UriComponentsBuilder; * * @author Rob Winch * @author Sam Brannen + * @author Rossen Stoyanchev * @since 4.2 * @see MockMvcWebConnection */ @@ -111,52 +110,58 @@ final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable { } + /** + * Set the contextPath to be used. + *

The value may be null in which case the first path segment of the + * URL is turned into the contextPath. Otherwise it must conform to + * {@link HttpServletRequest#getContextPath()} which states it can be + * an empty string, or it must start with a "/" and not end with a "/". + * @param contextPath a valid contextPath + * @throws IllegalArgumentException if the contextPath is not a valid + * {@link HttpServletRequest#getContextPath()} + */ + public void setContextPath(@Nullable String contextPath) { + MockMvcWebConnection.validateContextPath(contextPath); + this.contextPath = contextPath; + } + + public void setForwardPostProcessor(RequestPostProcessor forwardPostProcessor) { + this.forwardPostProcessor = forwardPostProcessor; + } + + @Override public MockHttpServletRequest buildRequest(ServletContext servletContext) { - Charset charset = getCharset(); String httpMethod = this.webRequest.getHttpMethod().name(); - UriComponents uriComponents = uriComponents(); - String path = uriComponents.getPath(); + UriComponents uri = UriComponentsBuilder.fromUriString(this.webRequest.getUrl().toExternalForm()).build(); - MockHttpServletRequest request = - new HtmlUnitMockHttpServletRequest(servletContext, httpMethod, (path != null ? path : "")); + MockHttpServletRequest request = new HtmlUnitMockHttpServletRequest( + servletContext, httpMethod, (uri.getPath() != null ? uri.getPath() : "")); parent(request, this.parentBuilder); - String host = uriComponents.getHost(); - request.setServerName(host != null ? host : ""); // needs to be first for additional headers - authType(request); - request.setCharacterEncoding(charset.name()); - content(request, charset); - contextPath(request, uriComponents); - contentType(request); - cookies(request); - headers(request); - locales(request); - servletPath(uriComponents, request); - params(request, uriComponents); - ports(uriComponents, request); + request.setProtocol("HTTP/1.1"); - request.setQueryString(uriComponents.getQuery()); - String scheme = uriComponents.getScheme(); - request.setScheme(scheme != null ? scheme : ""); + request.setScheme(uri.getScheme() != null ? uri.getScheme() : ""); + request.setServerName(uri.getHost() != null ? uri.getHost() : ""); // needs to be first for additional headers + ports(uri, request); + authType(request); + contextPath(request, uri); + servletPath(uri, request); request.setPathInfo(null); - return postProcess(request); - } - - private Charset getCharset() { Charset charset = this.webRequest.getCharset(); - return (charset != null ? charset : StandardCharsets.ISO_8859_1); - } + charset = (charset != null ? charset : StandardCharsets.ISO_8859_1); + request.setCharacterEncoding(charset.name()); + content(request, charset); + contentType(request); - private MockHttpServletRequest postProcess(MockHttpServletRequest request) { - if (this.parentPostProcessor != null) { - request = this.parentPostProcessor.postProcessRequest(request); - } - if (this.forwardPostProcessor != null) { - request = this.forwardPostProcessor.postProcessRequest(request); - } - return request; + cookies(request); + this.webRequest.getAdditionalHeaders().forEach(request::addHeader); + locales(request); + params(request, uri); + request.setQueryString(uri.getQuery()); + + return postProcess(request); } private void parent(MockHttpServletRequest request, @Nullable RequestBuilder parent) { @@ -208,50 +213,30 @@ final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable { } } - /** - * Set the contextPath to be used. - *

The value may be null in which case the first path segment of the - * URL is turned into the contextPath. Otherwise it must conform to - * {@link HttpServletRequest#getContextPath()} which states it can be - * an empty string, or it must start with a "/" and not end with a "/". - * @param contextPath a valid contextPath - * @throws IllegalArgumentException if the contextPath is not a valid - * {@link HttpServletRequest#getContextPath()} - */ - public void setContextPath(@Nullable String contextPath) { - MockMvcWebConnection.validateContextPath(contextPath); - this.contextPath = contextPath; - } - - public void setForwardPostProcessor(RequestPostProcessor forwardPostProcessor) { - this.forwardPostProcessor = forwardPostProcessor; + private void ports(UriComponents uriComponents, MockHttpServletRequest request) { + int serverPort = uriComponents.getPort(); + request.setServerPort(serverPort); + if (serverPort == -1) { + int portConnection = this.webRequest.getUrl().getDefaultPort(); + request.setLocalPort(serverPort); + request.setRemotePort(portConnection); + } + else { + request.setRemotePort(serverPort); + } } private void authType(MockHttpServletRequest request) { - String authorization = header("Authorization"); + String authorization = getHeader("Authorization"); String[] authSplit = StringUtils.split(authorization, ": "); if (authSplit != null) { request.setAuthType(authSplit[0]); } } - private void content(MockHttpServletRequest request, Charset charset) { - String requestBody = this.webRequest.getRequestBody(); - if (requestBody == null) { - return; - } - request.setContent(requestBody.getBytes(charset)); - } - - private void contentType(MockHttpServletRequest request) { - String contentType = header("Content-Type"); - if (contentType == null) { - FormEncodingType encodingType = this.webRequest.getEncodingType(); - if (encodingType != null) { - contentType = encodingType.getName(); - } - } - request.setContentType(contentType != null ? contentType : MediaType.ALL_VALUE); + @Nullable + private String getHeader(String headerName) { + return this.webRequest.getAdditionalHeaders().get(headerName); } private void contextPath(MockHttpServletRequest request, UriComponents uriComponents) { @@ -273,10 +258,36 @@ final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable { } } + private void servletPath(UriComponents uriComponents, MockHttpServletRequest request) { + String path = uriComponents.getPath(); + String requestPath = (path != null ? path : ""); + String servletPath = requestPath.substring(request.getContextPath().length()); + request.setServletPath(servletPath); + } + + private void content(MockHttpServletRequest request, Charset charset) { + String requestBody = this.webRequest.getRequestBody(); + if (requestBody == null) { + return; + } + request.setContent(requestBody.getBytes(charset)); + } + + private void contentType(MockHttpServletRequest request) { + String contentType = getHeader("Content-Type"); + if (contentType == null) { + FormEncodingType encodingType = this.webRequest.getEncodingType(); + if (encodingType != null) { + contentType = encodingType.getName(); + } + } + request.setContentType(contentType != null ? contentType : MediaType.ALL_VALUE); + } + private void cookies(MockHttpServletRequest request) { List cookies = new ArrayList<>(); - String cookieHeaderValue = header("Cookie"); + String cookieHeaderValue = getHeader("Cookie"); if (cookieHeaderValue != null) { StringTokenizer tokens = new StringTokenizer(cookieHeaderValue, "=;"); while (tokens.hasMoreTokens()) { @@ -312,15 +323,6 @@ final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable { } } - @Nullable - private String header(String headerName) { - return this.webRequest.getAdditionalHeaders().get(headerName); - } - - private void headers(MockHttpServletRequest request) { - this.webRequest.getAdditionalHeaders().forEach(request::addHeader); - } - private MockHttpSession httpSession(MockHttpServletRequest request, final String sessionid) { MockHttpSession session; synchronized (this.sessions) { @@ -341,11 +343,11 @@ final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable { } private void addSessionCookie(MockHttpServletRequest request, String sessionid) { - getCookieManager().addCookie(createCookie(request, sessionid)); + this.webClient.getCookieManager().addCookie(createCookie(request, sessionid)); } private void removeSessionCookie(MockHttpServletRequest request, String sessionid) { - getCookieManager().removeCookie(createCookie(request, sessionid)); + this.webClient.getCookieManager().removeCookie(createCookie(request, sessionid)); } private com.gargoylesoftware.htmlunit.util.Cookie createCookie(MockHttpServletRequest request, String sessionid) { @@ -354,7 +356,7 @@ final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable { } private void locales(MockHttpServletRequest request) { - String locale = header("Accept-Language"); + String locale = getHeader("Accept-Language"); if (locale == null) { request.addPreferredLocale(Locale.getDefault()); } @@ -407,36 +409,18 @@ final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable { } } - private void servletPath(MockHttpServletRequest request, String requestPath) { - String servletPath = requestPath.substring(request.getContextPath().length()); - request.setServletPath(servletPath); + private MockHttpServletRequest postProcess(MockHttpServletRequest request) { + if (this.parentPostProcessor != null) { + request = this.parentPostProcessor.postProcessRequest(request); + } + if (this.forwardPostProcessor != null) { + request = this.forwardPostProcessor.postProcessRequest(request); + } + return request; } - private void servletPath(UriComponents uriComponents, MockHttpServletRequest request) { - if ("".equals(request.getPathInfo())) { - request.setPathInfo(null); - } - String path = uriComponents.getPath(); - servletPath(request, (path != null ? path : "")); - } - private void ports(UriComponents uriComponents, MockHttpServletRequest request) { - int serverPort = uriComponents.getPort(); - request.setServerPort(serverPort); - if (serverPort == -1) { - int portConnection = this.webRequest.getUrl().getDefaultPort(); - request.setLocalPort(serverPort); - request.setRemotePort(portConnection); - } - else { - request.setRemotePort(serverPort); - } - } - - private UriComponents uriComponents() { - URL url = this.webRequest.getUrl(); - return UriComponentsBuilder.fromUriString(url.toExternalForm()).build(); - } + /* Mergeable methods */ @Override public boolean isMergeEnabled() { @@ -461,10 +445,6 @@ final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable { return this; } - private CookieManager getCookieManager() { - return this.webClient.getCookieManager(); - } - /** * An extension to {@link MockHttpServletRequest} that ensures that when a