diff --git a/pom.xml b/pom.xml index 1d55f8468..f112ba91d 100644 --- a/pom.xml +++ b/pom.xml @@ -152,12 +152,12 @@ commons-io commons-io - test + true com.google.guava guava - test + true com.jcabi @@ -213,7 +213,7 @@ commons-lang commons-lang 2.6 - test + true diff --git a/src/main/java/org/takes/rs/RsWithHeaders.java b/src/main/java/org/takes/rs/RsWithHeaders.java index 5f91c943d..5d72bb7b1 100644 --- a/src/main/java/org/takes/rs/RsWithHeaders.java +++ b/src/main/java/org/takes/rs/RsWithHeaders.java @@ -43,6 +43,14 @@ @EqualsAndHashCode(callSuper = true) public final class RsWithHeaders extends RsWrap { + /** + * Ctor. + * @param headers Headers + */ + public RsWithHeaders(final Iterable headers) { + this(new RsEmpty(), headers); + } + /** * Ctor. * @param res Original response diff --git a/src/main/java/org/takes/tk/TkProxy.java b/src/main/java/org/takes/tk/TkProxy.java index 2cee606ae..31b554d18 100644 --- a/src/main/java/org/takes/tk/TkProxy.java +++ b/src/main/java/org/takes/tk/TkProxy.java @@ -23,124 +23,135 @@ */ package org.takes.tk; +import com.google.common.net.HostAndPort; +import com.jcabi.http.request.ApacheRequest; import java.io.IOException; -import lombok.EqualsAndHashCode; -import lombok.ToString; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; import org.takes.Request; import org.takes.Response; import org.takes.Take; +import org.takes.rq.RqHeaders; +import org.takes.rq.RqHref; +import org.takes.rq.RqLengthAware; +import org.takes.rq.RqMethod; +import org.takes.rs.RsWithBody; +import org.takes.rs.RsWithHeaders; +import org.takes.rs.RsWithStatus; /** - * Proxy take. - * - *

This take may transform the request before passing it - * to the original take and/or transform the response received - * from the original take. - * - *

If the request transformer is not provided, the original - * request is passed to the original take. - * - *

If the response transformer is not provided, the response - * received from the original take is returned. + * Take that proxies requests to another destination. * *

The class is immutable and thread-safe. * * @author Dragan Bozanovic (bozanovicdr@gmail.com) * @version $Id$ * @since 0.25 + * @checkstyle ClassDataAbstractionCouplingCheck (500 lines) + * @todo #377 We need the integration test for this class. + * The test should verify the different HTTP methods (GET, POST, etc), + * as well as the different combinations of request/response headers. */ -@ToString -@EqualsAndHashCode(of = { "origin", "rqtransformer", "rstransformer" }) -public final class TkProxy implements Take { - - /** - * Original take. - */ - private final transient Take origin; +public class TkProxy implements Take { /** - * Request transformer. + * Target host to which requests are forwarded. */ - private final transient RqTransformer rqtransformer; - - /** - * Response transformer. - */ - private final transient RsTransformer rstransformer; + private final transient HostAndPort host; /** * Ctor. - * @param take Original take - * @param rqtransformr Request transformer - * @param rstransformr Response transformer + * @param target Target to which requests are forwarded */ - public TkProxy(final Take take, final RqTransformer rqtransformr, - final RsTransformer rstransformr) { - this.origin = take; - this.rqtransformer = rqtransformr; - this.rstransformer = rstransformr; + public TkProxy(final HostAndPort target) { + this.host = target; } - /** - * Ctor. - * @param take Original take - * @param rqtransformr Request transformer - */ - public TkProxy(final Take take, final RqTransformer rqtransformr) { - this(take, rqtransformr, new RsTransformer() { - @Override - public Response transform(final Response response) { - return response; + @Override + public final Response act(final Request req) throws IOException { + final RqHref.Base base = new RqHref.Base(req); + final String home = new RqHref.Smart(base).home().bare(); + final String dest = StringUtils.replace( + base.href().toString(), + home, + String.format("http://%s/", this.host.toString()) + ); + final String method = new RqMethod.Base(req).method(); + com.jcabi.http.Request proxied = new ApacheRequest(dest) + .method(method); + final RqHeaders.Base headers = new RqHeaders.Base(req); + for (final String name : headers.names()) { + if ("content-length".equals(name.toLowerCase(Locale.ENGLISH))) { + continue; + } + if (isHost(name)) { + proxied = proxied.header(name, this.host.toString()); + continue; } - }); + for (final String value : headers.header(name)) { + proxied = proxied.header(name, value); + } + } + if (Arrays.asList("POST", "PUT").contains(method)) { + proxied = proxied.body() + .set(IOUtils.toByteArray(new RqLengthAware(req).body())) + .back(); + } + return this.response(home, dest, proxied.fetch()); } /** - * Ctor. - * @param take Original take - * @param rstransformr Response transformer + * Creates the response received from the target host. + * + * @param home Home host + * @param dest Destination URL + * @param rsp Response received from the target host + * @return Response */ - public TkProxy(final Take take, final RsTransformer rstransformr) { - this(take, new RqTransformer() { - @Override - public Request transform(final Request request) { - return request; - } - }, rstransformr); - } - - @Override - public Response act(final Request req) throws IOException { - return this.rstransformer.transform( - this.origin.act( - this.rqtransformer.transform(req) + private Response response(final String home, final String dest, + final com.jcabi.http.Response rsp) { + final Collection hdrs = new LinkedList(); + hdrs.add( + String.format( + "X-Takes-TkProxy: from %s to %s", + home, dest ) ); + for (final Map.Entry> entry + : rsp.headers().entrySet()) { + for (final String value : entry.getValue()) { + final String val; + if (isHost(entry.getKey())) { + val = this.host.toString(); + } else { + val = value; + } + hdrs.add(String.format("%s: %s", entry.getKey(), val)); + } + } + return new RsWithStatus( + new RsWithBody( + new RsWithHeaders(hdrs), + rsp.binary() + ), + rsp.status(), + rsp.reason() + ); } /** - * Request transformer. - */ - public interface RqTransformer { - - /** - * Transforms the original request. - * @param request Original request - * @return The transformed request - */ - Request transform(Request request); - } - - /** - * Response transformer. + * Checks whether the provided argument is a "Host" header name. + * @param header Header name + * @return Returns {@code true} if {@code header} parameter is a "Host" + * header name, {@code false} otherwise */ - public interface RsTransformer { - - /** - * Transforms the original response. - * @param response Original response - * @return Transformed response - */ - Response transform(Response response); + private static boolean isHost(final String header) { + return "host".equals(header.toLowerCase(Locale.ENGLISH)); } } diff --git a/src/test/java/org/takes/tk/TkProxyTest.java b/src/test/java/org/takes/tk/TkProxyTest.java deleted file mode 100644 index 0337f31e0..000000000 --- a/src/test/java/org/takes/tk/TkProxyTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (c) 2015 Yegor Bugayenko - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package org.takes.tk; - -import java.io.IOException; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.Test; -import org.mockito.Mockito; -import org.takes.Request; -import org.takes.Response; -import org.takes.Take; -import org.takes.rq.RqFake; -import org.takes.rs.RsText; -import org.takes.tk.TkProxy.RqTransformer; -import org.takes.tk.TkProxy.RsTransformer; - -/** - * Test case for {@link org.takes.tk.TkProxy1}. - * @author Dragan Bozanovic (bozanovicdr@gmail.com) - * @version $Id$ - * @since 0.25 - */ -public final class TkProxyTest { - - /** - * TkProxy can proxy requests. - * @throws IOException If some problem inside - */ - @Test - public void proxiesRequest() throws IOException { - final Take take = Mockito.mock(Take.class); - final Request transformed = new RqFake("transformed request"); - final Response response = new RsText("response"); - Mockito.when(take.act(transformed)).thenReturn(response); - final TkProxy tkProxy = new TkProxy( - take, - new RqTransformer() { - @Override - public Request transform(final Request request) { - return transformed; - } - } - ); - MatcherAssert.assertThat( - tkProxy.act(new RqFake()), - Matchers.equalTo(response) - ); - Mockito.verify(take).act(transformed); - } - - /** - * TkProxy can proxy responses. - * @throws IOException If some problem inside - */ - @Test - public void proxiesResponse() throws IOException { - final Take take = Mockito.mock(Take.class); - final Request request = new RqFake("request"); - final Response transformed = new RsText("transformed response"); - final TkProxy tkProxy = new TkProxy( - take, - new RsTransformer() { - @Override - public Response transform(final Response response) { - return transformed; - } - } - ); - MatcherAssert.assertThat( - tkProxy.act(request), - Matchers.equalTo(transformed) - ); - Mockito.verify(take).act(request); - } -}