diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 12df014..24cc08a 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -19,11 +19,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v3 + - name: Set up JDK 21 + uses: actions/setup-java@v5 with: - java-version: '11' - distribution: 'adopt' + java-version: '21' + distribution: 'temurin' cache: maven - name: Build with Maven run: mvn -B clean install @@ -36,9 +36,9 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up Maven Central Repository - uses: actions/setup-java@v3 + uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '21' distribution: 'adopt' server-id: geosolutions server-username: MAVEN_USERNAME diff --git a/.gitignore b/.gitignore index 611a9d8..7441674 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ .project .settings */target/* -target/ \ No newline at end of file +target/ +.idea/ +main/assembly/* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5020776 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +# --- Build stage --- +FROM eclipse-temurin:21-jdk-alpine AS build + +RUN apk add --no-cache maven + +WORKDIR /app +COPY pom.xml . +RUN mvn dependency:go-offline -B + +COPY src ./src +RUN mvn clean package -Pdocker -DskipTests -B + +# --- Runtime stage --- +FROM gcr.io/distroless/java21-debian12 + +COPY --from=build /app/target/http-proxy.jar /app/http-proxy.jar + +ENV PORT=8080 +EXPOSE 8080 + +USER nonroot:nonroot +ENTRYPOINT ["java", "-jar", "/app/http-proxy.jar"] diff --git a/pom.xml b/pom.xml index ef7740e..0e065d6 100644 --- a/pom.xml +++ b/pom.xml @@ -27,17 +27,17 @@ maven-compiler-plugin - 3.5.1 + 3.13.0 - 8 - 8 + 21 + 21 org.apache.maven.plugins maven-surefire-plugin - 2.19.1 + 3.5.2 false @@ -46,7 +46,7 @@ org.apache.maven.plugins maven-jar-plugin - 2.6 + 3.4.2 make-a-jar @@ -61,7 +61,7 @@ org.apache.maven.plugins maven-install-plugin - 2.5.2 + 3.1.3 install @@ -82,7 +82,7 @@ org.apache.maven.plugins maven-deploy-plugin - 2.8.2 + 3.1.3 deploy @@ -107,7 +107,7 @@ org.apache.maven.plugins maven-war-plugin - 2.6 + 3.4.0 ${basedir}/src/main/webapp/WEB-INF/web.xml @@ -117,27 +117,16 @@ - org.mortbay.jetty - maven-jetty-plugin - 6.1.19 - - http_proxy - - - 8080 - 10000 - - - http_proxy - ${project.build.directory}/${project.artifactId}-${project.version} - + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + 12.1.9 org.apache.maven.plugins maven-release-plugin - 2.2.2 + 3.1.1 v@{project.version} @@ -155,8 +144,9 @@ UTF-8 - 2.19.0 - 4.5.13 + 2.25.4 + 4.5.14 + 12.1.8 @@ -173,19 +163,19 @@ org.apache.logging.log4j - log4j-slf4j-impl + log4j-slf4j2-impl ${log4j-version} - junit - junit - 4.12 + org.junit.jupiter + junit-jupiter + 5.11.4 test - commons-fileupload - commons-fileupload - 1.5 + org.apache.commons + commons-fileupload2-jakarta-servlet6 + 2.0.0-M5 @@ -203,58 +193,56 @@ commons-io commons-io - 2.1 + 2.18.0 commons-codec commons-codec - 1.4 + 1.17.1 - org.mortbay.jetty - servlet-api-2.5 - 6.1.14 + jakarta.servlet + jakarta.servlet-api + 6.1.0 + provided - - org.mortbay.jetty - jetty - 6.1.23 - test + org.eclipse.jetty.ee10 + jetty-ee10-webapp + ${jetty-version} - org.mortbay.jetty - jetty-servlet-tester - 6.1.5 + org.mockito + mockito-core + 5.14.2 test org.mockito - mockito-all - 1.9.5 + mockito-junit-jupiter + 5.14.2 test - - com.github.tomakehurst - wiremock-jre8-standalone - 2.32.0 + org.wiremock + wiremock-standalone + 3.10.0 test - + - junit - junit + org.junit.jupiter + junit-jupiter test @@ -269,13 +257,13 @@ org.apache.logging.log4j - log4j-slf4j-impl + log4j-slf4j2-impl - commons-fileupload - commons-fileupload + org.apache.commons + commons-fileupload2-jakarta-servlet6 @@ -299,38 +287,81 @@ - org.mortbay.jetty - servlet-api-2.5 + jakarta.servlet + jakarta.servlet-api - + - org.mortbay.jetty - jetty - test + org.eclipse.jetty.ee10 + jetty-ee10-webapp - - org.mortbay.jetty - jetty-servlet-tester - test - - + org.mockito - mockito-all + mockito-core test + + org.mockito + mockito-junit-jupiter + test + - com.github.tomakehurst - wiremock-jre8-standalone + org.wiremock + wiremock-standalone test + + + docker + + + jakarta.servlet + jakarta.servlet-api + 6.1.0 + compile + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.7.1 + + + + it.geosolutions.httpproxy.Main + + + + src/main/assembly/fat-jar.xml + + http-proxy + false + + + + make-assembly + package + + single + + + + + + + + + diff --git a/src/main/assembly/fat-jar.xml b/src/main/assembly/fat-jar.xml new file mode 100644 index 0000000..da608f4 --- /dev/null +++ b/src/main/assembly/fat-jar.xml @@ -0,0 +1,23 @@ + + fat-jar + + jar + + false + + + ${project.build.outputDirectory} + / + + + + + / + true + false + runtime + + + diff --git a/src/main/java/it/geosolutions/httpproxy/HTTPProxy.java b/src/main/java/it/geosolutions/httpproxy/HTTPProxy.java index e1a56a5..a04a616 100644 --- a/src/main/java/it/geosolutions/httpproxy/HTTPProxy.java +++ b/src/main/java/it/geosolutions/httpproxy/HTTPProxy.java @@ -33,24 +33,22 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.StringTokenizer; -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.FileUploadException; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; +import org.apache.commons.fileupload2.core.DiskFileItemFactory; +import org.apache.commons.fileupload2.core.FileItem; +import org.apache.commons.fileupload2.core.FileUploadException; +import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletFileUpload; import org.apache.http.Header; import org.apache.http.HttpEntity; -import org.apache.http.HttpException; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; @@ -67,7 +65,6 @@ import org.apache.http.entity.mime.content.ContentBody; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.DefaultProxyRoutePlanner; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.protocol.HttpContext; import org.slf4j.LoggerFactory; @@ -232,7 +229,7 @@ private HttpRoute getProxyRoute(HttpHost target) { }; } - private Boolean isNonProxyHost(String host) { + private boolean isNonProxyHost(String host) { boolean isNonProxyHost = false; String nonProxyHostProp = System.getProperty("http.nonProxyHosts"); if (nonProxyHostProp != null) { @@ -243,9 +240,8 @@ private Boolean isNonProxyHost(String host) { nonProxyHostProp = nonProxyHostProp.substring(0, nonProxyHostProp.length() - 1); } LOGGER.info("http.nonProxyHosts value: " + nonProxyHostProp); - StringTokenizer tokenizer = new StringTokenizer(nonProxyHostProp, "|"); - while (tokenizer.hasMoreTokens()) { - String str = tokenizer.nextToken().trim(); + for (String token : nonProxyHostProp.split("\\|")) { + String str = token.trim(); str = str.replace("*", "[\\w-]*"); Pattern pattern = Pattern.compile(str); Matcher matcher = pattern.matcher(host); @@ -304,12 +300,11 @@ public void doGet(HttpServletRequest httpServletRequest, HttpServletResponse htt URL url = null; String user = null, password = null; - Set entrySet = httpServletRequest.getParameterMap().entrySet(); + Set> entrySet = httpServletRequest.getParameterMap().entrySet(); - for (Object anEntrySet : entrySet) { - Map.Entry header = (Map.Entry) anEntrySet; - String key = (String) header.getKey(); - String value = ((String[]) header.getValue())[0]; + for (Map.Entry header : entrySet) { + String key = header.getKey(); + String value = header.getValue()[0]; if ("user".equals(key)) { user = value; @@ -405,7 +400,7 @@ public void doPost(HttpServletRequest httpServletRequest, // Check if this is a mulitpart (file upload) POST // ////////////////////////////////////////////////// - if (ServletFileUpload.isMultipartContent(httpServletRequest)) { + if (JakartaServletFileUpload.isMultipartContent(httpServletRequest)) { this.handleMultipart(postMethodProxyRequest, httpServletRequest); } else { this.handleStandard(postMethodProxyRequest, httpServletRequest); @@ -479,7 +474,7 @@ public void doPut(HttpServletRequest httpServletRequest, HttpServletResponse htt // Check if this is a mulitpart (file upload) PUT // ////////////////////////////////////////////////// - if (ServletFileUpload.isMultipartContent(httpServletRequest)) { + if (JakartaServletFileUpload.isMultipartContent(httpServletRequest)) { this.handleMultipart(putMethodProxyRequest, httpServletRequest); } else { this.handleStandard(putMethodProxyRequest, httpServletRequest); @@ -568,28 +563,25 @@ public void doDelete(HttpServletRequest httpServletRequest, * * @param postMethodProxyRequest The {@link PostMethod} that we are configuring to send a multipart POST request * @param httpServletRequest The {@link HttpServletRequest} that contains the mutlipart POST data to be sent via the {@link PostMethod} + * @throws IOException */ private void handleMultipart(HttpRequestBase methodProxyRequest, - HttpServletRequest httpServletRequest) throws ServletException { + HttpServletRequest httpServletRequest) throws ServletException, IOException { // //////////////////////////////////////////// // Create a factory for disk-based file items // //////////////////////////////////////////// - DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory(); - - // ///////////////////////////// - // Set factory constraints - // ///////////////////////////// - - diskFileItemFactory.setSizeThreshold(this.getMaxFileUploadSize()); - diskFileItemFactory.setRepository(Utils.DEFAULT_FILE_UPLOAD_TEMP_DIRECTORY); + DiskFileItemFactory diskFileItemFactory = DiskFileItemFactory.builder() + .setBufferSize(this.getMaxFileUploadSize()) + .setPath(Utils.DEFAULT_FILE_UPLOAD_TEMP_DIRECTORY.toPath()) + .get(); // ////////////////////////////////// // Create a new file upload handler // ////////////////////////////////// - ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory); + JakartaServletFileUpload servletFileUpload = new JakartaServletFileUpload(diskFileItemFactory); // ////////////////////////// // Parse the request @@ -601,7 +593,7 @@ private void handleMultipart(HttpRequestBase methodProxyRequest, // Get the multipart items as a list // ///////////////////////////////////// - List listFileItems = (List) servletFileUpload + List listFileItems = servletFileUpload .parseRequest(httpServletRequest); // ///////////////////////////////////////// @@ -693,8 +685,6 @@ private void executeProxyRequest(HttpRequestBase httpMethodProxyRequest, httpClient = createHttpClient(); } - InputStream inputStreamServerResponse = null; - try { // ////////////////////////// @@ -788,29 +778,21 @@ else if (header.getName().equalsIgnoreCase(Utils.HTTP_HEADER_TRANSFER_ENCODING)) // Send the content to the client // /////////////////////////////////// - inputStreamServerResponse = response.getEntity().getContent(); + try (InputStream inputStreamServerResponse = response.getEntity().getContent()) { + if (inputStreamServerResponse != null) { + byte[] b = new byte[proxyConfig.getDefaultStreamByteSize()]; - if (inputStreamServerResponse != null) { - byte[] b = new byte[proxyConfig.getDefaultStreamByteSize()]; - - int read = 0; - ServletOutputStream out = httpServletResponse.getOutputStream(); - while ((read = inputStreamServerResponse.read(b)) > 0) { - out.write(b, 0, read); + int read; + ServletOutputStream out = httpServletResponse.getOutputStream(); + while ((read = inputStreamServerResponse.read(b)) > 0) { + out.write(b, 0, read); + } } } } catch (Exception e) { LOGGER.error("Error executing HTTP method", e); } finally { - try { - if (inputStreamServerResponse != null) - inputStreamServerResponse.close(); - } catch (IOException e) { - LOGGER.error("Error closing request input stream", e); - throw new ServletException(e.getMessage()); - } - httpMethodProxyRequest.releaseConnection(); } } @@ -831,7 +813,6 @@ int getStatusCode(HttpResponse response) { * @param httpMethodProxyRequest The request that we are about to send to the proxy host * @return ProxyInfo */ - @SuppressWarnings("rawtypes") private ProxyInfo setProxyRequestHeaders(URL url, HttpServletRequest httpServletRequest, HttpRequestBase httpMethodProxyRequest) { @@ -845,7 +826,7 @@ private ProxyInfo setProxyRequestHeaders(URL url, HttpServletRequest httpServlet // names sent by the client. // //////////////////////////////////////// - Enumeration enumerationOfHeaderNames = httpServletRequest.getHeaderNames(); + Enumeration enumerationOfHeaderNames = httpServletRequest.getHeaderNames(); // //////////////////////////////////////// // Load header whitelist/blacklist for @@ -856,7 +837,7 @@ private ProxyInfo setProxyRequestHeaders(URL url, HttpServletRequest httpServlet Set headerBlacklist = proxyConfig.getRequestHeaderBlacklist(); while (enumerationOfHeaderNames.hasMoreElements()) { - String stringHeaderName = (String) enumerationOfHeaderNames.nextElement(); + String stringHeaderName = enumerationOfHeaderNames.nextElement(); if (stringHeaderName.equalsIgnoreCase(Utils.CONTENT_LENGTH_HEADER_NAME)) continue; @@ -890,10 +871,10 @@ private ProxyInfo setProxyRequestHeaders(URL url, HttpServletRequest httpServlet // Thus, we get an Enumeration of the header values sent by the client // //////////////////////////////////////////////////////////////////////// - Enumeration enumerationOfHeaderValues = httpServletRequest.getHeaders(stringHeaderName); + Enumeration enumerationOfHeaderValues = httpServletRequest.getHeaders(stringHeaderName); while (enumerationOfHeaderValues.hasMoreElements()) { - String stringHeaderValue = (String) enumerationOfHeaderValues.nextElement(); + String stringHeaderValue = enumerationOfHeaderValues.nextElement(); // //////////////////////////////////////////////////////////////// // In case the proxy host is running multiple virtual servers, diff --git a/src/main/java/it/geosolutions/httpproxy/HostChecker.java b/src/main/java/it/geosolutions/httpproxy/HostChecker.java index 28dd4e1..822c5f4 100644 --- a/src/main/java/it/geosolutions/httpproxy/HostChecker.java +++ b/src/main/java/it/geosolutions/httpproxy/HostChecker.java @@ -23,8 +23,8 @@ import java.net.URL; import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.http.client.methods.HttpRequestBase; diff --git a/src/main/java/it/geosolutions/httpproxy/HostNameChecker.java b/src/main/java/it/geosolutions/httpproxy/HostNameChecker.java index 5b24080..8d85943 100644 --- a/src/main/java/it/geosolutions/httpproxy/HostNameChecker.java +++ b/src/main/java/it/geosolutions/httpproxy/HostNameChecker.java @@ -23,8 +23,8 @@ import java.net.URL; import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.http.client.methods.HttpRequestBase; diff --git a/src/main/java/it/geosolutions/httpproxy/Main.java b/src/main/java/it/geosolutions/httpproxy/Main.java new file mode 100644 index 0000000..3a7b878 --- /dev/null +++ b/src/main/java/it/geosolutions/httpproxy/Main.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package it.geosolutions.httpproxy; + +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Main entry point for running the HTTP Proxy with embedded Jetty. + * Used for standalone deployment (fat JAR / Docker). + */ +public class Main { + + private static final Logger LOGGER = LoggerFactory.getLogger(Main.class); + + public static void main(String[] args) throws Exception { + int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "8080")); + + Server server = new Server(); + ServerConnector connector = new ServerConnector(server); + connector.setPort(port); + server.addConnector(connector); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/"); + + // Set the proxyPropPath init parameter + String proxyPropPath = System.getenv().getOrDefault("PROXY_PROP_PATH", "/proxy.properties"); + context.setInitParameter("proxyPropPath", proxyPropPath); + + // Register the proxy servlet + context.addServlet(new ServletHolder(new HTTPProxy()), "/proxy/*"); + + server.setHandler(context); + server.start(); + LOGGER.info("HTTP Proxy started on port {}", port); + server.join(); + } +} diff --git a/src/main/java/it/geosolutions/httpproxy/MethodsChecker.java b/src/main/java/it/geosolutions/httpproxy/MethodsChecker.java index 16a8d4d..821a893 100644 --- a/src/main/java/it/geosolutions/httpproxy/MethodsChecker.java +++ b/src/main/java/it/geosolutions/httpproxy/MethodsChecker.java @@ -23,8 +23,8 @@ import java.net.URL; import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.http.client.methods.HttpRequestBase; diff --git a/src/main/java/it/geosolutions/httpproxy/MimeTypeChecker.java b/src/main/java/it/geosolutions/httpproxy/MimeTypeChecker.java index 3ec1c60..59d8478 100644 --- a/src/main/java/it/geosolutions/httpproxy/MimeTypeChecker.java +++ b/src/main/java/it/geosolutions/httpproxy/MimeTypeChecker.java @@ -23,8 +23,8 @@ import java.net.URL; import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.http.Header; import org.apache.http.client.methods.HttpRequestBase; diff --git a/src/main/java/it/geosolutions/httpproxy/ProxyCallback.java b/src/main/java/it/geosolutions/httpproxy/ProxyCallback.java index b7febfb..2b4c62e 100644 --- a/src/main/java/it/geosolutions/httpproxy/ProxyCallback.java +++ b/src/main/java/it/geosolutions/httpproxy/ProxyCallback.java @@ -22,8 +22,8 @@ import java.io.IOException; import java.net.URL; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.http.client.methods.HttpRequestBase; diff --git a/src/main/java/it/geosolutions/httpproxy/ProxyConfig.java b/src/main/java/it/geosolutions/httpproxy/ProxyConfig.java index 267a0ea..fefb1ec 100644 --- a/src/main/java/it/geosolutions/httpproxy/ProxyConfig.java +++ b/src/main/java/it/geosolutions/httpproxy/ProxyConfig.java @@ -26,10 +26,11 @@ import java.util.HashSet; import java.util.Properties; import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.servlet.ServletContext; +import jakarta.servlet.ServletContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * ProxyConfig class to define the proxy configuration. @@ -38,7 +39,7 @@ */ final class ProxyConfig { - private final static Logger LOGGER = Logger.getLogger(ProxyConfig.class.toString()); + private static final Logger LOGGER = LoggerFactory.getLogger(ProxyConfig.class); /** * A list of regular expressions describing hostnames the proxy is permitted to forward to @@ -215,9 +216,7 @@ private void configProxy() { : this.defaultMaxConnectionsPerHost); } catch (NumberFormatException e) { - if (LOGGER.isLoggable(Level.SEVERE)) - LOGGER.log(Level.SEVERE, - "Error parsing the proxy properties file using default", e); + LOGGER.error("Error parsing proxy configuration: {}", e.getMessage(), e); this.setSoTimeout(this.soTimeout); this.setConnectionTimeout(this.connectionTimeout); @@ -248,25 +247,16 @@ private void mergePropertiesConfig(String path, Properties properties) { try { inputStream = new FileInputStream(path); } catch (FileNotFoundException e) { - if (LOGGER.isLoggable(Level.WARNING)) - LOGGER.log(Level.WARNING, "The properties file " + path + " cannot be found"); + LOGGER.warn("The properties file {} cannot be found", path); } } if (inputStream != null) { - Properties props = new Properties(); - try { - props.load(inputStream); + try (InputStream is = inputStream) { + Properties props = new Properties(); + props.load(is); properties.putAll(props); } catch (IOException e) { - if (LOGGER.isLoggable(Level.SEVERE)) - LOGGER.log(Level.SEVERE, "Error loading the proxy properties file from " + path, e); - } finally { - if (inputStream != null) - try { - inputStream.close(); - } catch (IOException e) { - - } + LOGGER.error("Error loading the proxy properties file from {}", path, e); } } } diff --git a/src/main/java/it/geosolutions/httpproxy/RequestTypeChecker.java b/src/main/java/it/geosolutions/httpproxy/RequestTypeChecker.java index 901343d..b48a08d 100644 --- a/src/main/java/it/geosolutions/httpproxy/RequestTypeChecker.java +++ b/src/main/java/it/geosolutions/httpproxy/RequestTypeChecker.java @@ -26,8 +26,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.http.client.methods.HttpRequestBase; diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index b04a3af..618282f 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -1,5 +1,5 @@ - + HTTP_PROXY diff --git a/src/test/java/it/geosolutions/httpproxy/HttpProxyIntegrationTests.java b/src/test/java/it/geosolutions/httpproxy/HttpProxyIntegrationTests.java index 0cc2fe7..9370b32 100644 --- a/src/test/java/it/geosolutions/httpproxy/HttpProxyIntegrationTests.java +++ b/src/test/java/it/geosolutions/httpproxy/HttpProxyIntegrationTests.java @@ -2,7 +2,10 @@ import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.junit5.WireMockExtension; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; @@ -10,12 +13,8 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; -import org.junit.*; -import org.mortbay.jetty.Connector; -import org.mortbay.jetty.Server; -import org.mortbay.jetty.bio.SocketConnector; -import org.mortbay.jetty.webapp.WebAppContext; -import org.mortbay.thread.BoundedThreadPool; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.LoggerFactory; import java.io.File; @@ -30,13 +29,15 @@ public class HttpProxyIntegrationTests { private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(HttpProxyIntegrationTests.class); - @Rule - public WireMockRule wireMockRule = - new WireMockRule(WireMockConfiguration.options().dynamicPort()); + @RegisterExtension + static WireMockExtension wireMockRule = WireMockExtension.newInstance() + .options(WireMockConfiguration.options().dynamicPort()) + .build(); - @Rule - public WireMockRule wireMockRule1 = - new WireMockRule(WireMockConfiguration.options().dynamicPort()); + @RegisterExtension + static WireMockExtension wireMockRule1 = WireMockExtension.newInstance() + .options(WireMockConfiguration.options().dynamicPort()) + .build(); static WireMockServer wireMockServer; @@ -45,7 +46,7 @@ public class HttpProxyIntegrationTests { private static String proxyHost; private static String proxyPort; - @BeforeClass + @BeforeAll public static void startHttpProxyServer() { try { @@ -56,23 +57,15 @@ public static void startHttpProxyServer() { wireMockServer.start(); jettyServer = new Server(); + ServerConnector connector = new ServerConnector(jettyServer); + connector.setPort(8080); + jettyServer.addConnector(connector); - BoundedThreadPool tp = new BoundedThreadPool(); - tp.setMaxThreads(50); - - SocketConnector conn = new SocketConnector(); - int port = 8080; - - conn.setPort(port); - conn.setThreadPool(tp); - conn.setAcceptQueueSize(100); - jettyServer.setConnectors(new Connector[]{conn}); - - WebAppContext wah = new WebAppContext(); - wah.setContextPath("/http_proxy"); - wah.setWar("src/main/webapp"); - jettyServer.setHandler(wah); - wah.setTempDirectory(new File("target/work")); + WebAppContext webapp = new WebAppContext(); + webapp.setContextPath("/http_proxy"); + webapp.setWar(new File("src/main/webapp").getAbsolutePath()); + webapp.setTempDirectory(new File("target/work")); + jettyServer.setHandler(webapp); jettyServer.start(); @@ -103,21 +96,20 @@ private static void storeProxyConfig() { @Test public void testGETUsingWireMock() throws IOException { - wireMockRule.addStubMapping( - stubFor( - get(urlEqualTo("/geostore/users")) - .willReturn( - aResponse() - .withStatus(200) - .withHeader("Content-Type", "text/xml") - .withBody("Some content")))); + wireMockRule.stubFor( + get(urlEqualTo("/geostore/users")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/xml") + .withBody("Some content"))); - String url = "http://localhost:" + wireMockRule.port() + "/geostore/users"; + String url = "http://localhost:" + wireMockRule.getPort() + "/geostore/users"; String proxyURL = "http://localhost:" + localPort + "/http_proxy/proxy?url=" + url; HttpGet httpGet = new HttpGet(proxyURL); try (CloseableHttpClient httpClient = HttpClients.createDefault()) { HttpResponse httpResponse = httpClient.execute(httpGet); - Assert.assertEquals(200, httpResponse.getStatusLine().getStatusCode()); + Assertions.assertEquals(200, httpResponse.getStatusLine().getStatusCode()); wireMockRule.verify(getRequestedFor(urlEqualTo("/geostore/users"))); } } @@ -129,17 +121,16 @@ public void testGETUsingWireMock() throws IOException { public void testPOSTUsingWireMockXML() throws IOException { String requestBody = "5Jane Doe"; - wireMockRule1.addStubMapping( - stubFor( - post(urlEqualTo("/geostore/users/create")) - .withRequestBody(equalToXml(requestBody)) - .willReturn( - aResponse() - .withStatus(201) - .withHeader("Content-Type", "text/xml") - .withBody("5")))); - - String url = "http://localhost:" + wireMockRule1.port() + "/geostore/users/create"; + wireMockRule1.stubFor( + post(urlEqualTo("/geostore/users/create")) + .withRequestBody(equalToXml(requestBody)) + .willReturn( + aResponse() + .withStatus(201) + .withHeader("Content-Type", "text/xml") + .withBody("5"))); + + String url = "http://localhost:" + wireMockRule1.getPort() + "/geostore/users/create"; String proxyURL = "http://localhost:" + localPort + "/http_proxy/proxy?url=" + url; HttpPost httpPost = new HttpPost(proxyURL); StringEntity stringEntity = new StringEntity(requestBody); @@ -147,8 +138,8 @@ public void testPOSTUsingWireMockXML() throws IOException { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { HttpResponse httpResponse = httpClient.execute(httpPost); - Assert.assertEquals("text/xml", httpResponse.getFirstHeader("Content-Type").getValue()); - Assert.assertEquals(201, httpResponse.getStatusLine().getStatusCode()); + Assertions.assertEquals("text/xml", httpResponse.getFirstHeader("Content-Type").getValue()); + Assertions.assertEquals(201, httpResponse.getStatusLine().getStatusCode()); wireMockRule1.verify(postRequestedFor(urlEqualTo("/geostore/users/create"))); } } @@ -165,19 +156,18 @@ public void testPOSTUsingWireMockJSON() throws IOException { " \"userName\": \"Jane Doe\"\n" + " }\n" + "}"; - wireMockRule1.addStubMapping( - stubFor( - post(urlEqualTo("/geostore/users/create")) - .withRequestBody(equalToJson(requestBody)) - .willReturn( - aResponse() - .withStatus(201) - .withHeader("Content-Type", "application/json") - .withBody("{\n" + - " \"response\": 5\n" + - "}")))); - - String url = "http://localhost:" + wireMockRule1.port() + "/geostore/users/create"; + wireMockRule1.stubFor( + post(urlEqualTo("/geostore/users/create")) + .withRequestBody(equalToJson(requestBody)) + .willReturn( + aResponse() + .withStatus(201) + .withHeader("Content-Type", "application/json") + .withBody("{\n" + + " \"response\": 5\n" + + "}"))); + + String url = "http://localhost:" + wireMockRule1.getPort() + "/geostore/users/create"; String proxyURL = "http://localhost:" + localPort + "/http_proxy/proxy?url=" + url; HttpPost httpPost = new HttpPost(proxyURL); StringEntity stringEntity = new StringEntity(requestBody); @@ -185,8 +175,8 @@ public void testPOSTUsingWireMockJSON() throws IOException { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { HttpResponse httpResponse = httpClient.execute(httpPost); - Assert.assertEquals("application/json", httpResponse.getFirstHeader("Content-Type").getValue()); - Assert.assertEquals(201, httpResponse.getStatusLine().getStatusCode()); + Assertions.assertEquals("application/json", httpResponse.getFirstHeader("Content-Type").getValue()); + Assertions.assertEquals(201, httpResponse.getStatusLine().getStatusCode()); wireMockRule1.verify(postRequestedFor(urlEqualTo("/geostore/users/create"))); } } @@ -198,17 +188,16 @@ public void testPOSTUsingWireMockJSON() throws IOException { public void testPUTUsingWireMock() throws IOException { String requestBody = "5John Doe"; - wireMockRule.addStubMapping( - stubFor( - put(urlEqualTo("/geostore/users/5")) - .withRequestBody(equalToXml(requestBody)) - .willReturn( - aResponse() - .withStatus(200) - .withHeader("Content-Type", "text/xml") - .withBody("5")))); - - String url = "http://localhost:" + wireMockRule.port() + "/geostore/users/5"; + wireMockRule.stubFor( + put(urlEqualTo("/geostore/users/5")) + .withRequestBody(equalToXml(requestBody)) + .willReturn( + aResponse() + .withStatus(200) + .withHeader("Content-Type", "text/xml") + .withBody("5"))); + + String url = "http://localhost:" + wireMockRule.getPort() + "/geostore/users/5"; String proxyURL = "http://localhost:" + localPort + "/http_proxy/proxy?url=" + url; HttpPut httpPut = new HttpPut(proxyURL); StringEntity stringEntity = new StringEntity(requestBody); @@ -216,7 +205,7 @@ public void testPUTUsingWireMock() throws IOException { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { HttpResponse httpResponse = httpClient.execute(httpPut); - Assert.assertEquals(200, httpResponse.getStatusLine().getStatusCode()); + Assertions.assertEquals(200, httpResponse.getStatusLine().getStatusCode()); wireMockRule.verify(putRequestedFor(urlEqualTo("/geostore/users/5"))); } } @@ -231,19 +220,18 @@ public void testNullProxyConfig() throws IOException { System.setProperty("http.proxyHost", ""); System.setProperty("http.proxyPort", ""); - wireMockRule.addStubMapping( - stubFor( - get(urlEqualTo("/geostore/resources")) - .willReturn( - aResponse() - .withStatus(200)))); + wireMockRule.stubFor( + get(urlEqualTo("/geostore/resources")) + .willReturn( + aResponse() + .withStatus(200))); - String url = "http://localhost:" + wireMockRule.port() + "/geostore/resources"; + String url = "http://localhost:" + wireMockRule.getPort() + "/geostore/resources"; String proxyURL = "http://localhost:" + localPort + "/http_proxy/proxy?url=" + url; HttpGet httpGet = new HttpGet(proxyURL); try (CloseableHttpClient httpClient = HttpClients.createDefault()) { HttpResponse httpResponse = httpClient.execute(httpGet); - Assert.assertEquals(200, httpResponse.getStatusLine().getStatusCode()); + Assertions.assertEquals(200, httpResponse.getStatusLine().getStatusCode()); wireMockRule.verify(getRequestedFor(urlEqualTo("/geostore/resources"))); } } @@ -255,10 +243,10 @@ public void testNullProxyConfig() throws IOException { public void testHttpDefaultPort() throws IOException { String urlStr = "http://localhost/geoserver/wms?service=wms%26version=1.3.0&request=GetCapabilities"; URL url = Utils.buildURL(urlStr); - Assert.assertEquals(url.getPort(), Utils.DEFAULT_HTTP_PORT); - Assert.assertEquals(url.getProtocol(), "http"); - Assert.assertEquals(url.getHost(), "localhost"); - Assert.assertEquals(url.getFile(), "/geoserver/wms?service=wms%26version=1.3.0&request=GetCapabilities"); + Assertions.assertEquals(url.getPort(), Utils.DEFAULT_HTTP_PORT); + Assertions.assertEquals(url.getProtocol(), "http"); + Assertions.assertEquals(url.getHost(), "localhost"); + Assertions.assertEquals(url.getFile(), "/geoserver/wms?service=wms%26version=1.3.0&request=GetCapabilities"); } /** @@ -268,14 +256,14 @@ public void testHttpDefaultPort() throws IOException { public void testHttpsDefaultPort() throws IOException { String urlStr = "https://georchestra.geo-solutions.it/geoserver/wms?service=WMS%26version=1.3.0&request=GetCapabilities"; URL url = Utils.buildURL(urlStr); - Assert.assertEquals(url.getPort(), Utils.DEFAULT_HTTPS_PORT); - Assert.assertEquals(url.getProtocol(), "https"); - Assert.assertEquals(url.getHost(), "georchestra.geo-solutions.it"); - Assert.assertEquals(url.getFile(), "/geoserver/wms?service=WMS%26version=1.3.0&request=GetCapabilities"); + Assertions.assertEquals(url.getPort(), Utils.DEFAULT_HTTPS_PORT); + Assertions.assertEquals(url.getProtocol(), "https"); + Assertions.assertEquals(url.getHost(), "georchestra.geo-solutions.it"); + Assertions.assertEquals(url.getFile(), "/geoserver/wms?service=WMS%26version=1.3.0&request=GetCapabilities"); } - @AfterClass + @AfterAll public static void stopServer() { try { diff --git a/src/test/java/it/geosolutions/httpproxy/HttpProxyTest.java b/src/test/java/it/geosolutions/httpproxy/HttpProxyTest.java index d1371aa..59a33b4 100644 --- a/src/test/java/it/geosolutions/httpproxy/HttpProxyTest.java +++ b/src/test/java/it/geosolutions/httpproxy/HttpProxyTest.java @@ -29,11 +29,11 @@ import java.util.List; import java.util.Map; -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.http.Header; import org.apache.http.HttpEntity; @@ -44,28 +44,29 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.message.BasicHeader; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.mockito.Mockito.*; /** * HttpProxyTest class. Test Cases for the HTTPProxy servlet. * * @author Lorenzo Natali at lorenzo.natali@geo-solutions.it */ -public class HttpProxyTest extends Mockito { +public class HttpProxyTest { final ServletConfig servletConfig = mock(ServletConfig.class); ServletContext ctx = mock(ServletContext.class); - Map parameters = new HashMap(); - private List
headers = new ArrayList
(); + Map parameters = new HashMap<>(); + private List headers = new ArrayList<>(); org.apache.http.client.HttpClient mockHttpClient; HTTPProxy proxy; String fakeLocation; - @Before + @BeforeEach public void setUp() { File f = new File(getClass().getClassLoader() .getResource("test-proxy.properties").getFile()); @@ -123,8 +124,8 @@ public HttpGet getGetMethod(URL url) { "http://proxy.com/http-proxy/proxy?url=" + URLEncoder.encode(fakeLocation, "UTF-8")); final byte[] data = servletOutputStream.baos.toByteArray(); - Assert.assertNotNull(data); - Assert.assertTrue(data.length == 0); + Assertions.assertNotNull(data); + Assertions.assertTrue(data.length == 0); } @Test @@ -159,7 +160,7 @@ public HttpPost getPostMethod(URL url) { when(postRequest.getQueryString()).thenReturn("url=https://jsonplaceholder.typicode.com/test/createUser"); when(postRequest.getMethod()).thenReturn("post"); - Enumeration enumeration = Collections.enumeration(Collections.emptyList()); + Enumeration enumeration = Collections.enumeration(Collections.emptyList()); when(postRequest.getHeaderNames()).thenReturn(enumeration); ServletInputStream stream = mock(ServletInputStream.class); @@ -173,8 +174,8 @@ public HttpPost getPostMethod(URL url) { proxy.doPost(postRequest, getResponse); verify(getResponse).setStatus(200); final byte[] data = servletOutputStream.baos.toByteArray(); - Assert.assertNotNull(data); - Assert.assertTrue(data.length != 0); + Assertions.assertNotNull(data); + Assertions.assertTrue(data.length != 0); } } \ No newline at end of file diff --git a/src/test/java/it/geosolutions/httpproxy/RequestHeaderFilterTest.java b/src/test/java/it/geosolutions/httpproxy/RequestHeaderFilterTest.java index ffd6e6a..f95aea1 100644 --- a/src/test/java/it/geosolutions/httpproxy/RequestHeaderFilterTest.java +++ b/src/test/java/it/geosolutions/httpproxy/RequestHeaderFilterTest.java @@ -28,10 +28,10 @@ import java.util.List; import java.util.Map; -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.http.Header; import org.apache.http.HttpEntity; @@ -40,23 +40,19 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.entity.StringEntity; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static org.mockito.Mockito.*; /** * Tests for request header whitelist and blacklist filtering. */ -public class RequestHeaderFilterTest extends Mockito { +public class RequestHeaderFilterTest { Map parameters = new HashMap<>(); - private List
headers = new ArrayList<>(); - @Before + @BeforeEach public void setUp() { // URL must match one of the reqtypeWhitelist patterns in proxy.properties parameters.put("url", new String[]{"http://sample.com/csw"}); diff --git a/src/test/java/it/geosolutions/httpproxy/StubServletOutputStream.java b/src/test/java/it/geosolutions/httpproxy/StubServletOutputStream.java index d296996..936c990 100644 --- a/src/test/java/it/geosolutions/httpproxy/StubServletOutputStream.java +++ b/src/test/java/it/geosolutions/httpproxy/StubServletOutputStream.java @@ -3,11 +3,17 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; -import javax.servlet.ServletOutputStream; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.WriteListener; public class StubServletOutputStream extends ServletOutputStream { public ByteArrayOutputStream baos = new ByteArrayOutputStream(); public void write(int i) throws IOException { baos.write(i); + } + public boolean isReady() { + return true; + } + public void setWriteListener(WriteListener writeListener) { } } \ No newline at end of file diff --git a/src/test/java/it/geosolutions/httpproxy/jetty/Start.java b/src/test/java/it/geosolutions/httpproxy/jetty/Start.java index a3d0753..f103864 100644 --- a/src/test/java/it/geosolutions/httpproxy/jetty/Start.java +++ b/src/test/java/it/geosolutions/httpproxy/jetty/Start.java @@ -19,24 +19,20 @@ */ package it.geosolutions.httpproxy.jetty; -import java.io.File; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.mortbay.jetty.Connector; -import org.mortbay.jetty.Server; -import org.mortbay.jetty.bio.SocketConnector; -import org.mortbay.jetty.webapp.WebAppContext; -import org.mortbay.thread.BoundedThreadPool; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * Start class for test using JETTY server. + * Start class for test using embedded Jetty server. * * @author Tobia di Pisa at tobia.dipisa@geo-solutions.it */ public class Start { - private final static Logger LOGGER = Logger.getLogger(Start.class.toString()); + private static final Logger LOGGER = LoggerFactory.getLogger(Start.class); /** * @param args @@ -47,16 +43,7 @@ public static void main(String[] args) { try { jettyServer = new Server(); - // ///////////////////////////////////////////////////// - // Don't even think of serving more than XX requests - // in parallel... we have a limit in our processing - // and memory capacities. - // ///////////////////////////////////////////////////// - - BoundedThreadPool tp = new BoundedThreadPool(); - tp.setMaxThreads(50); - - SocketConnector conn = new SocketConnector(); + ServerConnector connector = new ServerConnector(jettyServer); String portVariable = System.getProperty("jetty.port"); int port = parsePort(portVariable); @@ -64,37 +51,27 @@ public static void main(String[] args) { port = 8080; } - conn.setPort(port); - conn.setThreadPool(tp); - conn.setAcceptQueueSize(100); - jettyServer.setConnectors(new Connector[] { conn }); + connector.setPort(port); + jettyServer.addConnector(connector); - WebAppContext wah = new WebAppContext(); - wah.setContextPath("/http_proxy"); - wah.setWar("src/main/webapp"); - jettyServer.setHandler(wah); - wah.setTempDirectory(new File("target/work")); + WebAppContext webapp = new WebAppContext(); + webapp.setContextPath("/http_proxy"); + webapp.setWar("src/main/webapp"); + webapp.setTempDirectory(new java.io.File("target/work")); + jettyServer.setHandler(webapp); jettyServer.start(); - - // //////////////////////////////////////////////////////////////////////// - // Use this to test normal stop behavior, that is, to check stuff that - // need to be done on container shutdown (and yes, this will make - // jetty stop just after you started it...) - // jettyServer.stop(); - // //////////////////////////////////////////////////////////////////////// + LOGGER.info("Jetty started on port {}", port); + jettyServer.join(); } catch (Exception e) { - if (LOGGER.isLoggable(Level.SEVERE)) - LOGGER.log(Level.SEVERE, "Could not start the Jetty server: " + e.getMessage(), e); + LOGGER.error("Could not start the Jetty server: " + e.getMessage(), e); if (jettyServer != null) { try { jettyServer.stop(); } catch (Exception e1) { - if (LOGGER.isLoggable(Level.SEVERE)) - LOGGER.log(Level.SEVERE, - "Unable to stop the " + "Jetty server:" + e1.getMessage(), e1); + LOGGER.error("Unable to stop the Jetty server: " + e1.getMessage(), e1); } } } @@ -110,7 +87,7 @@ private static int parsePort(String portVariable) { } try { - return Integer.valueOf(portVariable).intValue(); + return Integer.parseInt(portVariable); } catch (NumberFormatException e) { return -1; }