diff --git a/http/rest-client-reactive/pom.xml b/http/rest-client-reactive/pom.xml index 6737db606..206baec67 100644 --- a/http/rest-client-reactive/pom.xml +++ b/http/rest-client-reactive/pom.xml @@ -28,4 +28,31 @@ commons-lang3 + + + + skip-tests-on-windows + + + windows + + + + + + maven-surefire-plugin + + true + + + + maven-failsafe-plugin + + true + + + + + + diff --git a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/proxy/ProxyClient.java b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/proxy/ProxyClient.java new file mode 100644 index 000000000..fb9db8265 --- /dev/null +++ b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/proxy/ProxyClient.java @@ -0,0 +1,31 @@ +package io.quarkus.ts.http.restclient.reactive.proxy; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +import io.smallrye.mutiny.Uni; + +@RegisterRestClient +@RegisterClientHeaders +public interface ProxyClient { + + @GET + @Path("/") + @Produces(MediaType.TEXT_HTML) + Uni getSite(); + + @GET + @Path("/example.txt") + @Produces(MediaType.TEXT_PLAIN) + Uni getText(); + + @GET + @Path("/auth") + @Produces(MediaType.TEXT_PLAIN) + Uni getAuthorized(); +} diff --git a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/proxy/ProxyResource.java b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/proxy/ProxyResource.java new file mode 100644 index 000000000..29214137f --- /dev/null +++ b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/proxy/ProxyResource.java @@ -0,0 +1,41 @@ +package io.quarkus.ts.http.restclient.reactive.proxy; + +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.eclipse.microprofile.rest.client.inject.RestClient; + +import io.smallrye.mutiny.Uni; + +@Path("/proxied") +public class ProxyResource { + + @Inject + @RestClient + ProxyClient client; + + @GET + @Path("/") + @Produces(MediaType.TEXT_HTML) + public Uni getRoot() { + return client.getSite(); + } + + @GET + @Path("/banned") + @Produces(MediaType.TEXT_PLAIN) + public Uni getBanned() { + return client.getText(); + } + + @GET + @Path("/authorization") + @Produces(MediaType.TEXT_PLAIN) + public Uni getAuthorized() { + return client.getAuthorized(); + } + +} diff --git a/http/rest-client-reactive/src/main/resources/proxy.properties b/http/rest-client-reactive/src/main/resources/proxy.properties new file mode 100644 index 000000000..075eab02d --- /dev/null +++ b/http/rest-client-reactive/src/main/resources/proxy.properties @@ -0,0 +1,6 @@ +quarkus.rest-client."io.quarkus.ts.http.restclient.reactive.proxy.ProxyClient".url=http://example.com +quarkus.rest-client."io.quarkus.ts.http.restclient.reactive.proxy.ProxyClient".proxy-address=localhost:8090 +quarkus.rest-client."io.quarkus.ts.http.restclient.reactive.proxy.ProxyClient".proxy-user=proxyuser +quarkus.rest-client."io.quarkus.ts.http.restclient.reactive.proxy.ProxyClient".proxy-password=proxypassword +quarkus.rest-client.logging.scope=request-response +quarkus.log.category."org.jboss.resteasy.reactive.client.logging".level=DEBUG diff --git a/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/ProxyIT.java b/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/ProxyIT.java new file mode 100644 index 000000000..b81c55dbb --- /dev/null +++ b/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/ProxyIT.java @@ -0,0 +1,65 @@ +package io.quarkus.ts.http.restclient.reactive; + +import java.util.Base64; + +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.bootstrap.RestService; +import io.quarkus.test.scenarios.QuarkusScenario; +import io.quarkus.test.services.Container; +import io.quarkus.test.services.QuarkusApplication; +import io.restassured.response.Response; + +@QuarkusScenario +public class ProxyIT { + private static final String USER = "proxyuser"; + private static final String PASSWORD = "proxypassword"; + + @Container(image = "quay.io/quarkusqeteam/proxy", port = 8090, expectedLog = "Configuration complete; ready for start up") + static RestService proxy = new RestService(); + + @QuarkusApplication + static RestService proxyApp = new RestService() + .withProperties("proxy.properties") + .withProperty("quarkus.rest-client.\"io.quarkus.ts.http.restclient.reactive.proxy.ProxyClient\".proxy-user", USER) + .withProperty("quarkus.rest-client.\"io.quarkus.ts.http.restclient.reactive.proxy.ProxyClient\".proxy-password", + PASSWORD) + .withProperty("quarkus.rest-client.\"io.quarkus.ts.http.restclient.reactive.proxy.ProxyClient\".proxy-address", + () -> proxy.getHost().replace("http://", "") + ":" + proxy.getPort()); + + @Test + void getThrough() { + Response proxied = proxyApp.given().with().get("/proxied/"); + Assertions.assertEquals(HttpStatus.SC_OK, proxied.statusCode()); + Assertions.assertTrue(proxied.body().asString().contains("Example Domain")); + } + + @Test + void banned() { + Response banned = proxyApp.given().with().get("/proxied/banned"); + Assertions.assertEquals(HttpStatus.SC_OK, banned.statusCode()); + Assertions.assertEquals("Reading is prohibited by corporate policy!", + banned.body().asString()); + } + + @Test + /* + * Nginx returns content of Proxy-Auth Header. + * We check, that this content is made according to the specification. + * https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#proxy_authentication + */ + void authorization() { + Response response = proxyApp.given().with().get("/proxied/authorization"); + Assertions.assertEquals(HttpStatus.SC_OK, response.statusCode()); + String authorizationType = "Basic"; + String credentials = encode(USER + ":" + PASSWORD); + String header = response.body().asString(); + Assertions.assertEquals(authorizationType + " " + credentials, header); + } + + private static String encode(String source) { + return Base64.getEncoder().encodeToString(source.getBytes()); + } +} diff --git a/http/rest-client-reactive/src/test/resources/proxy/Dockerfile b/http/rest-client-reactive/src/test/resources/proxy/Dockerfile new file mode 100644 index 000000000..1cc9e078d --- /dev/null +++ b/http/rest-client-reactive/src/test/resources/proxy/Dockerfile @@ -0,0 +1,2 @@ +FROM nginx:1.21 +COPY nginx.conf /etc/nginx/nginx.conf diff --git a/http/rest-client-reactive/src/test/resources/proxy/README.md b/http/rest-client-reactive/src/test/resources/proxy/README.md new file mode 100644 index 000000000..ac394c376 --- /dev/null +++ b/http/rest-client-reactive/src/test/resources/proxy/README.md @@ -0,0 +1,11 @@ +This folder contains everything for building and deploying proxy container, based on nginx +It is based on these resources: +* http://nginx.org/en/docs/beginners_guide.html#proxy +* http://nginx.org/en/docs/http/ngx_http_proxy_module.html +* https://www.baeldung.com/nginx-forward-proxy + +By default, the container listens on port 8090. + +Script run.sh is mainly for debugging + +Script deploy.sh is for building and deploying to quay.io diff --git a/http/rest-client-reactive/src/test/resources/proxy/deploy.sh b/http/rest-client-reactive/src/test/resources/proxy/deploy.sh new file mode 100755 index 000000000..cd3749b89 --- /dev/null +++ b/http/rest-client-reactive/src/test/resources/proxy/deploy.sh @@ -0,0 +1,3 @@ +docker build -t nginx-proxy . +docker tag nginx-proxy quay.io/quarkusqeteam/proxy:latest +docker push quay.io/quarkusqeteam/proxy:latest diff --git a/http/rest-client-reactive/src/test/resources/proxy/nginx.conf b/http/rest-client-reactive/src/test/resources/proxy/nginx.conf new file mode 100644 index 000000000..cf7b3ba6c --- /dev/null +++ b/http/rest-client-reactive/src/test/resources/proxy/nginx.conf @@ -0,0 +1,16 @@ +events {} +http { + server { + listen 8090; + resolver 8.8.8.8; + location / { + proxy_pass http://$host; + } + location ~ \.(txt)$ { + return 203 "Reading is prohibited by corporate policy!"; + } + location /auth { + return 200 $http_proxy_authorization; + } + } +} diff --git a/http/rest-client-reactive/src/test/resources/proxy/run.sh b/http/rest-client-reactive/src/test/resources/proxy/run.sh new file mode 100755 index 000000000..a78c7075b --- /dev/null +++ b/http/rest-client-reactive/src/test/resources/proxy/run.sh @@ -0,0 +1 @@ +docker build -t nginx-proxy . && docker run -p 8090:8090 nginx-proxy